pub trait TargetIsa: Display + Send + Sync {
Show 14 methods fn name(&self) -> &'static str; fn triple(&self) -> &Triple; fn flags(&self) -> &Flags; fn machine_env(&self) -> &MachineEnv; fn isa_flags(&self) -> Vec<Value>; fn dynamic_vector_bytes(&self, dynamic_ty: Type) -> u32; fn compile_function(
        &self,
        func: &Function,
        want_disasm: bool
    ) -> CodegenResult<CompiledCodeBase<Stencil>>; fn unsigned_add_overflow_condition(&self) -> IntCC; fn emit_unwind_info(
        &self,
        result: &CompiledCode,
        kind: UnwindInfoKind
    ) -> CodegenResult<Option<UnwindInfo>>; fn text_section_builder(
        &self,
        num_labeled_funcs: usize
    ) -> Box<dyn TextSectionBuilder>; fn function_alignment(&self) -> u32; fn is_branch_protection_enabled(&self) -> bool { ... } fn map_regalloc_reg_to_dwarf(
        &self,
        _: Reg
    ) -> Result<u16, RegisterMappingError> { ... } fn create_systemv_cie(&self) -> Option<CommonInformationEntry> { ... }
}
Expand description

Methods that are specialized to a target ISA.

Implies a Display trait that shows the shared flags, as well as any ISA-specific flags.

Required Methods§

Get the name of this ISA.

Get the target triple that was used to make this trait object.

Get the ISA-independent flags that were used to make this trait object.

Get the ISA-dependent MachineEnv for managing register allocation.

Get the ISA-dependent flag values that were used to make this trait object.

Get the ISA-dependent maximum vector register size, in bytes.

Compile the given function.

IntCC condition for Unsigned Addition Overflow (Carry).

Creates unwind information for the function.

Returns None if there is no unwind information for the function.

Returns an object that can be used to build the text section of an executable.

This object will internally attempt to handle as many relocations as possible using relative calls/jumps/etc between functions.

The num_labeled_funcs argument here is the number of functions which will be “labeled” or might have calls between them, typically the number of defined functions in the object file.

The function alignment required by this ISA.

Provided Methods§

Get a flag indicating whether branch protection is enabled.

Map a regalloc::Reg to its corresponding DWARF register.

Creates a new System V Common Information Entry for the ISA.

Returns None if the ISA does not support System V unwind information.

Implementations§

Methods implemented for free for target ISA!

Get the default calling convention of this target.

Examples found in repository?
src/isa/mod.rs (line 362)
360
361
362
363
364
365
    pub fn frontend_config(&self) -> TargetFrontendConfig {
        TargetFrontendConfig {
            default_call_conv: self.default_call_conv(),
            pointer_width: self.pointer_width(),
        }
    }

Get the endianness of this ISA.

Returns the code (text) section alignment for this ISA.

Returns the minimum symbol alignment for this ISA.

Get the pointer type of this ISA.

Examples found in repository?
src/ir/globalvalue.rs (line 100)
98
99
100
101
102
103
104
    pub fn global_type(&self, isa: &dyn TargetIsa) -> Type {
        match *self {
            Self::VMContext { .. } | Self::Symbol { .. } => isa.pointer_type(),
            Self::IAddImm { global_type, .. } | Self::Load { global_type, .. } => global_type,
            Self::DynScaleTargetConst { .. } => isa.pointer_type(),
        }
    }
More examples
Hide additional examples
src/legalizer/globalvalue.rs (line 51)
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
fn const_vector_scale(inst: ir::Inst, func: &mut ir::Function, ty: ir::Type, isa: &dyn TargetIsa) {
    assert!(ty.bytes() <= 16);

    // Use a minimum of 128-bits for the base type.
    let base_bytes = std::cmp::max(ty.bytes(), 16);
    let scale = (isa.dynamic_vector_bytes(ty) / base_bytes) as i64;
    assert!(scale > 0);
    let pos = FuncCursor::new(func).at_inst(inst);
    pos.func.dfg.replace(inst).iconst(isa.pointer_type(), scale);
}

/// Expand a `global_value` instruction for a vmctx global.
fn vmctx_addr(inst: ir::Inst, func: &mut ir::Function) {
    // Get the value representing the `vmctx` argument.
    let vmctx = func
        .special_param(ir::ArgumentPurpose::VMContext)
        .expect("Missing vmctx parameter");

    // Replace the `global_value` instruction's value with an alias to the vmctx arg.
    let result = func.dfg.first_result(inst);
    func.dfg.clear_results(inst);
    func.dfg.change_to_alias(result, vmctx);
    func.layout.remove_inst(inst);
}

/// Expand a `global_value` instruction for an iadd_imm global.
fn iadd_imm_addr(
    inst: ir::Inst,
    func: &mut ir::Function,
    base: ir::GlobalValue,
    offset: i64,
    global_type: ir::Type,
) {
    let mut pos = FuncCursor::new(func).at_inst(inst);

    // Get the value for the lhs. For tidiness, expand VMContext here so that we avoid
    // `vmctx_addr` which creates an otherwise unneeded value alias.
    let lhs = if let ir::GlobalValueData::VMContext = pos.func.global_values[base] {
        pos.func
            .special_param(ir::ArgumentPurpose::VMContext)
            .expect("Missing vmctx parameter")
    } else {
        pos.ins().global_value(global_type, base)
    };

    // Simply replace the `global_value` instruction with an `iadd_imm`, reusing the result value.
    pos.func.dfg.replace(inst).iadd_imm(lhs, offset);
}

/// Expand a `global_value` instruction for a load global.
fn load_addr(
    inst: ir::Inst,
    func: &mut ir::Function,
    base: ir::GlobalValue,
    offset: ir::immediates::Offset32,
    global_type: ir::Type,
    readonly: bool,
    isa: &dyn TargetIsa,
) {
    // We need to load a pointer from the `base` global value, so insert a new `global_value`
    // instruction. This depends on the iterative legalization loop. Note that the IR verifier
    // detects any cycles in the `load` globals.
    let ptr_ty = isa.pointer_type();
    let mut pos = FuncCursor::new(func).at_inst(inst);
    pos.use_srcloc(inst);

    // Get the value for the base. For tidiness, expand VMContext here so that we avoid
    // `vmctx_addr` which creates an otherwise unneeded value alias.
    let base_addr = if let ir::GlobalValueData::VMContext = pos.func.global_values[base] {
        pos.func
            .special_param(ir::ArgumentPurpose::VMContext)
            .expect("Missing vmctx parameter")
    } else {
        pos.ins().global_value(ptr_ty, base)
    };

    // Global-value loads are always notrap and aligned. They may be readonly.
    let mut mflags = ir::MemFlags::trusted();
    if readonly {
        mflags.set_readonly();
    }

    // Perform the load.
    pos.func
        .dfg
        .replace(inst)
        .load(global_type, mflags, base_addr, offset);
}

/// Expand a `global_value` instruction for a symbolic name global.
fn symbol(
    inst: ir::Inst,
    func: &mut ir::Function,
    gv: ir::GlobalValue,
    isa: &dyn TargetIsa,
    tls: bool,
) {
    let ptr_ty = isa.pointer_type();

    if tls {
        func.dfg.replace(inst).tls_value(ptr_ty, gv);
    } else {
        func.dfg.replace(inst).symbol_value(ptr_ty, gv);
    }
}
src/verifier/mod.rs (line 390)
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
    fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        let mut cycle_seen = false;
        let mut seen = SparseSet::new();

        'gvs: for gv in self.func.global_values.keys() {
            seen.clear();
            seen.insert(gv);

            let mut cur = gv;
            loop {
                match self.func.global_values[cur] {
                    ir::GlobalValueData::Load { base, .. }
                    | ir::GlobalValueData::IAddImm { base, .. } => {
                        if seen.insert(base).is_some() {
                            if !cycle_seen {
                                errors.report((
                                    gv,
                                    format!("global value cycle: {}", DisplayList(seen.as_slice())),
                                ));
                                // ensures we don't report the cycle multiple times
                                cycle_seen = true;
                            }
                            continue 'gvs;
                        }

                        cur = base;
                    }
                    _ => break,
                }
            }

            match self.func.global_values[gv] {
                ir::GlobalValueData::VMContext { .. } => {
                    if self
                        .func
                        .special_param(ir::ArgumentPurpose::VMContext)
                        .is_none()
                    {
                        errors.report((gv, format!("undeclared vmctx reference {}", gv)));
                    }
                }
                ir::GlobalValueData::IAddImm {
                    base, global_type, ..
                } => {
                    if !global_type.is_int() {
                        errors.report((
                            gv,
                            format!("iadd_imm global value with non-int type {}", global_type),
                        ));
                    } else if let Some(isa) = self.isa {
                        let base_type = self.func.global_values[base].global_type(isa);
                        if global_type != base_type {
                            errors.report((
                                gv,
                                format!(
                                    "iadd_imm type {} differs from operand type {}",
                                    global_type, base_type
                                ),
                            ));
                        }
                    }
                }
                ir::GlobalValueData::Load { base, .. } => {
                    if let Some(isa) = self.isa {
                        let base_type = self.func.global_values[base].global_type(isa);
                        let pointer_type = isa.pointer_type();
                        if base_type != pointer_type {
                            errors.report((
                                gv,
                                format!(
                                    "base {} has type {}, which is not the pointer type {}",
                                    base, base_type, pointer_type
                                ),
                            ));
                        }
                    }
                }
                _ => {}
            }
        }

        // Invalid global values shouldn't stop us from verifying the rest of the function
        Ok(())
    }

    fn verify_heaps(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        if let Some(isa) = self.isa {
            for (heap, heap_data) in &self.func.heaps {
                let base = heap_data.base;
                if !self.func.global_values.is_valid(base) {
                    return errors.nonfatal((heap, format!("invalid base global value {}", base)));
                }

                let pointer_type = isa.pointer_type();
                let base_type = self.func.global_values[base].global_type(isa);
                if base_type != pointer_type {
                    errors.report((
                        heap,
                        format!(
                            "heap base has type {}, which is not the pointer type {}",
                            base_type, pointer_type
                        ),
                    ));
                }

                if let ir::HeapStyle::Dynamic { bound_gv, .. } = heap_data.style {
                    if !self.func.global_values.is_valid(bound_gv) {
                        return errors
                            .nonfatal((heap, format!("invalid bound global value {}", bound_gv)));
                    }

                    let bound_type = self.func.global_values[bound_gv].global_type(isa);
                    if pointer_type != bound_type {
                        errors.report((
                            heap,
                            format!(
                                "heap pointer type {} differs from the type of its bound, {}",
                                pointer_type, bound_type
                            ),
                        ));
                    }
                }
            }
        }

        Ok(())
    }

    fn verify_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        if let Some(isa) = self.isa {
            for (table, table_data) in &self.func.tables {
                let base = table_data.base_gv;
                if !self.func.global_values.is_valid(base) {
                    return errors.nonfatal((table, format!("invalid base global value {}", base)));
                }

                let pointer_type = isa.pointer_type();
                let base_type = self.func.global_values[base].global_type(isa);
                if base_type != pointer_type {
                    errors.report((
                        table,
                        format!(
                            "table base has type {}, which is not the pointer type {}",
                            base_type, pointer_type
                        ),
                    ));
                }

                let bound_gv = table_data.bound_gv;
                if !self.func.global_values.is_valid(bound_gv) {
                    return errors
                        .nonfatal((table, format!("invalid bound global value {}", bound_gv)));
                }

                let index_type = table_data.index_type;
                let bound_type = self.func.global_values[bound_gv].global_type(isa);
                if index_type != bound_type {
                    errors.report((
                        table,
                        format!(
                            "table index type {} differs from the type of its bound, {}",
                            index_type, bound_type
                        ),
                    ));
                }
            }
        }

        Ok(())
    }
src/legalizer/mod.rs (line 97)
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) {
    trace!("Pre-legalization function:\n{}", func.display());

    let mut pos = FuncCursor::new(func);
    let func_begin = pos.position();
    pos.set_position(func_begin);
    while let Some(_block) = pos.next_block() {
        let mut prev_pos = pos.position();
        while let Some(inst) = pos.next_inst() {
            match pos.func.dfg[inst] {
                // control flow
                InstructionData::CondTrap {
                    opcode:
                        opcode @ (ir::Opcode::Trapnz | ir::Opcode::Trapz | ir::Opcode::ResumableTrapnz),
                    arg,
                    code,
                } => {
                    expand_cond_trap(inst, &mut pos.func, cfg, opcode, arg, code);
                }

                // memory and constants
                InstructionData::UnaryGlobalValue {
                    opcode: ir::Opcode::GlobalValue,
                    global_value,
                } => expand_global_value(inst, &mut pos.func, isa, global_value),
                InstructionData::HeapAddr {
                    opcode: ir::Opcode::HeapAddr,
                    heap,
                    arg,
                    offset,
                    size,
                } => expand_heap_addr(inst, &mut pos.func, cfg, isa, heap, arg, offset, size),
                InstructionData::HeapLoad {
                    opcode: ir::Opcode::HeapLoad,
                    heap_imm,
                    arg,
                } => expand_heap_load(inst, &mut pos.func, cfg, isa, heap_imm, arg),
                InstructionData::HeapStore {
                    opcode: ir::Opcode::HeapStore,
                    heap_imm,
                    args,
                } => expand_heap_store(inst, &mut pos.func, cfg, isa, heap_imm, args[0], args[1]),
                InstructionData::StackLoad {
                    opcode: ir::Opcode::StackLoad,
                    stack_slot,
                    offset,
                } => {
                    let ty = pos.func.dfg.value_type(pos.func.dfg.first_result(inst));
                    let addr_ty = isa.pointer_type();

                    let mut pos = FuncCursor::new(pos.func).at_inst(inst);
                    pos.use_srcloc(inst);

                    let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);

                    // Stack slots are required to be accessible and aligned.
                    let mflags = MemFlags::trusted();
                    pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
                }
                InstructionData::StackStore {
                    opcode: ir::Opcode::StackStore,
                    arg,
                    stack_slot,
                    offset,
                } => {
                    let addr_ty = isa.pointer_type();

                    let mut pos = FuncCursor::new(pos.func).at_inst(inst);
                    pos.use_srcloc(inst);

                    let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);

                    let mut mflags = MemFlags::new();
                    // Stack slots are required to be accessible and aligned.
                    mflags.set_notrap();
                    mflags.set_aligned();
                    pos.func.dfg.replace(inst).store(mflags, arg, addr, 0);
                }
                InstructionData::DynamicStackLoad {
                    opcode: ir::Opcode::DynamicStackLoad,
                    dynamic_stack_slot,
                } => {
                    let ty = pos.func.dfg.value_type(pos.func.dfg.first_result(inst));
                    assert!(ty.is_dynamic_vector());
                    let addr_ty = isa.pointer_type();

                    let mut pos = FuncCursor::new(pos.func).at_inst(inst);
                    pos.use_srcloc(inst);

                    let addr = pos.ins().dynamic_stack_addr(addr_ty, dynamic_stack_slot);

                    // Stack slots are required to be accessible and aligned.
                    let mflags = MemFlags::trusted();
                    pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
                }
                InstructionData::DynamicStackStore {
                    opcode: ir::Opcode::DynamicStackStore,
                    arg,
                    dynamic_stack_slot,
                } => {
                    pos.use_srcloc(inst);
                    let addr_ty = isa.pointer_type();
                    let vector_ty = pos.func.dfg.value_type(arg);
                    assert!(vector_ty.is_dynamic_vector());

                    let addr = pos.ins().dynamic_stack_addr(addr_ty, dynamic_stack_slot);

                    let mut mflags = MemFlags::new();
                    // Stack slots are required to be accessible and aligned.
                    mflags.set_notrap();
                    mflags.set_aligned();
                    pos.func.dfg.replace(inst).store(mflags, arg, addr, 0);
                }
                InstructionData::TableAddr {
                    opcode: ir::Opcode::TableAddr,
                    table,
                    arg,
                    offset,
                } => expand_table_addr(isa, inst, &mut pos.func, table, arg, offset),

                InstructionData::BinaryImm64 { opcode, arg, imm } => {
                    let is_signed = match opcode {
                        ir::Opcode::IaddImm
                        | ir::Opcode::IrsubImm
                        | ir::Opcode::ImulImm
                        | ir::Opcode::SdivImm
                        | ir::Opcode::SremImm
                        | ir::Opcode::IfcmpImm => true,
                        _ => false,
                    };

                    let imm = imm_const(&mut pos, arg, imm, is_signed);
                    let replace = pos.func.dfg.replace(inst);
                    match opcode {
                        // bitops
                        ir::Opcode::BandImm => {
                            replace.band(arg, imm);
                        }
                        ir::Opcode::BorImm => {
                            replace.bor(arg, imm);
                        }
                        ir::Opcode::BxorImm => {
                            replace.bxor(arg, imm);
                        }
                        // bitshifting
                        ir::Opcode::IshlImm => {
                            replace.ishl(arg, imm);
                        }
                        ir::Opcode::RotlImm => {
                            replace.rotl(arg, imm);
                        }
                        ir::Opcode::RotrImm => {
                            replace.rotr(arg, imm);
                        }
                        ir::Opcode::SshrImm => {
                            replace.sshr(arg, imm);
                        }
                        ir::Opcode::UshrImm => {
                            replace.ushr(arg, imm);
                        }
                        // math
                        ir::Opcode::IaddImm => {
                            replace.iadd(arg, imm);
                        }
                        ir::Opcode::IrsubImm => {
                            // note: arg order reversed
                            replace.isub(imm, arg);
                        }
                        ir::Opcode::ImulImm => {
                            replace.imul(arg, imm);
                        }
                        ir::Opcode::SdivImm => {
                            replace.sdiv(arg, imm);
                        }
                        ir::Opcode::SremImm => {
                            replace.srem(arg, imm);
                        }
                        ir::Opcode::UdivImm => {
                            replace.udiv(arg, imm);
                        }
                        ir::Opcode::UremImm => {
                            replace.urem(arg, imm);
                        }
                        // comparisons
                        ir::Opcode::IfcmpImm => {
                            replace.ifcmp(arg, imm);
                        }
                        _ => prev_pos = pos.position(),
                    };
                }

                // comparisons
                InstructionData::IntCompareImm {
                    opcode: ir::Opcode::IcmpImm,
                    cond,
                    arg,
                    imm,
                } => {
                    let imm = imm_const(&mut pos, arg, imm, true);
                    pos.func.dfg.replace(inst).icmp(cond, arg, imm);
                }

                _ => {
                    prev_pos = pos.position();
                    continue;
                }
            }

            // Legalization implementations require fixpoint loop here.
            // TODO: fix this.
            pos.set_position(prev_pos);
        }
    }

    trace!("Post-legalization function:\n{}", func.display());
}
src/legalizer/heap.rs (line 116)
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
fn bounds_check_and_compute_addr(
    pos: &mut FuncCursor,
    cfg: &mut ControlFlowGraph,
    isa: &dyn TargetIsa,
    heap: ir::Heap,
    // Dynamic operand indexing into the heap.
    index: ir::Value,
    // Static immediate added to the index.
    offset: u32,
    // Static size of the heap access.
    access_size: u8,
) -> ir::Value {
    let pointer_type = isa.pointer_type();
    let spectre = isa.flags().enable_heap_access_spectre_mitigation();
    let offset_and_size = offset_plus_size(offset, access_size);

    let ir::HeapData {
        base: _,
        min_size,
        offset_guard_size: guard_size,
        style,
        index_type,
    } = pos.func.heaps[heap].clone();

    let index = cast_index_to_pointer_ty(index, index_type, pointer_type, pos);

    // We need to emit code that will trap (or compute an address that will trap
    // when accessed) if
    //
    //     index + offset + access_size > bound
    //
    // or if the `index + offset + access_size` addition overflows.
    //
    // Note that we ultimately want a 64-bit integer (we only target 64-bit
    // architectures at the moment) and that `offset` is a `u32` and
    // `access_size` is a `u8`. This means that we can add the latter together
    // as `u64`s without fear of overflow, and we only have to be concerned with
    // whether adding in `index` will overflow.
    //
    // Finally, the following right-hand sides of the matches do have a little
    // bit of duplicated code across them, but I think writing it this way is
    // worth it for readability and seeing very clearly each of our cases for
    // different bounds checks and optimizations of those bounds checks. It is
    // intentionally written in a straightforward case-matching style that will
    // hopefully make it easy to port to ISLE one day.
    match style {
        // ====== Dynamic Memories ======
        //
        // 1. First special case for when `offset + access_size == 1`:
        //
        //            index + 1 > bound
        //        ==> index >= bound
        //
        //    1.a. When Spectre mitigations are enabled, avoid duplicating
        //         bounds checks between the mitigations and the regular bounds
        //         checks.
        ir::HeapStyle::Dynamic { bound_gv } if offset_and_size == 1 && spectre => {
            let bound = pos.ins().global_value(pointer_type, bound_gv);
            compute_addr(
                isa,
                pos,
                heap,
                pointer_type,
                index,
                offset,
                Some(SpectreOobComparison {
                    cc: IntCC::UnsignedGreaterThanOrEqual,
                    lhs: index,
                    rhs: bound,
                }),
            )
        }
        //    1.b. Emit explicit `index >= bound` bounds checks.
        ir::HeapStyle::Dynamic { bound_gv } if offset_and_size == 1 => {
            let bound = pos.ins().global_value(pointer_type, bound_gv);
            let oob = pos
                .ins()
                .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
            pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
            compute_addr(isa, pos, heap, pointer_type, index, offset, None)
        }

        // 2. Second special case for when `offset + access_size <= min_size`.
        //
        //    We know that `bound >= min_size`, so we can do the following
        //    comparison, without fear of the right-hand side wrapping around:
        //
        //            index + offset + access_size > bound
        //        ==> index > bound - (offset + access_size)
        //
        //    2.a. Dedupe bounds checks with Spectre mitigations.
        ir::HeapStyle::Dynamic { bound_gv } if offset_and_size <= min_size.into() && spectre => {
            let bound = pos.ins().global_value(pointer_type, bound_gv);
            let adjusted_bound = pos.ins().iadd_imm(bound, -(offset_and_size as i64));
            compute_addr(
                isa,
                pos,
                heap,
                pointer_type,
                index,
                offset,
                Some(SpectreOobComparison {
                    cc: IntCC::UnsignedGreaterThan,
                    lhs: index,
                    rhs: adjusted_bound,
                }),
            )
        }
        //    2.b. Emit explicit `index > bound - (offset + access_size)` bounds
        //         checks.
        ir::HeapStyle::Dynamic { bound_gv } if offset_and_size <= min_size.into() => {
            let bound = pos.ins().global_value(pointer_type, bound_gv);
            let adjusted_bound = pos.ins().iadd_imm(bound, -(offset_and_size as i64));
            let oob = pos
                .ins()
                .icmp(IntCC::UnsignedGreaterThan, index, adjusted_bound);
            pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
            compute_addr(isa, pos, heap, pointer_type, index, offset, None)
        }

        // 3. General case for dynamic memories:
        //
        //        index + offset + access_size > bound
        //
        //    And we have to handle the overflow case in the left-hand side.
        //
        //    3.a. Dedupe bounds checks with Spectre mitigations.
        ir::HeapStyle::Dynamic { bound_gv } if spectre => {
            let access_size_val = pos.ins().iconst(pointer_type, offset_and_size as i64);
            let adjusted_index =
                pos.ins()
                    .uadd_overflow_trap(index, access_size_val, ir::TrapCode::HeapOutOfBounds);
            let bound = pos.ins().global_value(pointer_type, bound_gv);
            compute_addr(
                isa,
                pos,
                heap,
                pointer_type,
                index,
                offset,
                Some(SpectreOobComparison {
                    cc: IntCC::UnsignedGreaterThan,
                    lhs: adjusted_index,
                    rhs: bound,
                }),
            )
        }
        //    3.b. Emit an explicit `index + offset + access_size > bound`
        //         check.
        ir::HeapStyle::Dynamic { bound_gv } => {
            let access_size_val = pos.ins().iconst(pointer_type, offset_and_size as i64);
            let adjusted_index =
                pos.ins()
                    .uadd_overflow_trap(index, access_size_val, ir::TrapCode::HeapOutOfBounds);
            let bound = pos.ins().global_value(pointer_type, bound_gv);
            let oob = pos
                .ins()
                .icmp(IntCC::UnsignedGreaterThan, adjusted_index, bound);
            pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
            compute_addr(isa, pos, heap, pointer_type, index, offset, None)
        }

        // ====== Static Memories ======
        //
        // With static memories we know the size of the heap bound at compile
        // time.
        //
        // 1. First special case: trap immediately if `offset + access_size >
        //    bound`, since we will end up being out-of-bounds regardless of the
        //    given `index`.
        ir::HeapStyle::Static { bound } if offset_and_size > bound.into() => {
            pos.ins().trap(ir::TrapCode::HeapOutOfBounds);

            // Split the block, as the trap is a terminator instruction.
            let curr_block = pos.current_block().expect("Cursor is not in a block");
            let new_block = pos.func.dfg.make_block();
            pos.insert_block(new_block);
            cfg.recompute_block(pos.func, curr_block);
            cfg.recompute_block(pos.func, new_block);

            let null = pos.ins().iconst(pointer_type, 0);
            return null;
        }

        // 2. Second special case for when we can completely omit explicit
        //    bounds checks for 32-bit static memories.
        //
        //    First, let's rewrite our comparison to move all of the constants
        //    to one side:
        //
        //            index + offset + access_size > bound
        //        ==> index > bound - (offset + access_size)
        //
        //    We know the subtraction on the right-hand side won't wrap because
        //    we didn't hit the first special case.
        //
        //    Additionally, we add our guard pages (if any) to the right-hand
        //    side, since we can rely on the virtual memory subsystem at runtime
        //    to catch out-of-bound accesses within the range `bound .. bound +
        //    guard_size`. So now we are dealing with
        //
        //        index > bound + guard_size - (offset + access_size)
        //
        //    Note that `bound + guard_size` cannot overflow for
        //    correctly-configured heaps, as otherwise the heap wouldn't fit in
        //    a 64-bit memory space.
        //
        //    The complement of our should-this-trap comparison expression is
        //    the should-this-not-trap comparison expression:
        //
        //        index <= bound + guard_size - (offset + access_size)
        //
        //    If we know the right-hand side is greater than or equal to
        //    `u32::MAX`, then
        //
        //        index <= u32::MAX <= bound + guard_size - (offset + access_size)
        //
        //    This expression is always true when the heap is indexed with
        //    32-bit integers because `index` cannot be larger than
        //    `u32::MAX`. This means that `index` is always either in bounds or
        //    within the guard page region, neither of which require emitting an
        //    explicit bounds check.
        ir::HeapStyle::Static { bound }
            if index_type == ir::types::I32
                && u64::from(u32::MAX)
                    <= u64::from(bound) + u64::from(guard_size) - offset_and_size =>
        {
            compute_addr(isa, pos, heap, pointer_type, index, offset, None)
        }

        // 3. General case for static memories.
        //
        //    We have to explicitly test whether
        //
        //        index > bound - (offset + access_size)
        //
        //    and trap if so.
        //
        //    Since we have to emit explicit bounds checks, we might as well be
        //    precise, not rely on the virtual memory subsystem at all, and not
        //    factor in the guard pages here.
        //
        //    3.a. Dedupe the Spectre mitigation and the explicit bounds check.
        ir::HeapStyle::Static { bound } if spectre => {
            // NB: this subtraction cannot wrap because we didn't hit the first
            // special case.
            let adjusted_bound = u64::from(bound) - offset_and_size;
            let adjusted_bound = pos.ins().iconst(pointer_type, adjusted_bound as i64);
            compute_addr(
                isa,
                pos,
                heap,
                pointer_type,
                index,
                offset,
                Some(SpectreOobComparison {
                    cc: IntCC::UnsignedGreaterThan,
                    lhs: index,
                    rhs: adjusted_bound,
                }),
            )
        }
        //    3.b. Emit the explicit `index > bound - (offset + access_size)`
        //         check.
        ir::HeapStyle::Static { bound } => {
            // See comment in 3.a. above.
            let adjusted_bound = u64::from(bound) - offset_and_size;
            let oob = pos
                .ins()
                .icmp_imm(IntCC::UnsignedGreaterThan, index, adjusted_bound as i64);
            pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
            compute_addr(isa, pos, heap, pointer_type, index, offset, None)
        }
    }
}

fn cast_index_to_pointer_ty(
    index: ir::Value,
    index_ty: ir::Type,
    pointer_ty: ir::Type,
    pos: &mut FuncCursor,
) -> ir::Value {
    if index_ty == pointer_ty {
        return index;
    }
    // Note that using 64-bit heaps on a 32-bit host is not currently supported,
    // would require at least a bounds check here to ensure that the truncation
    // from 64-to-32 bits doesn't lose any upper bits. For now though we're
    // mostly interested in the 32-bit-heaps-on-64-bit-hosts cast.
    assert!(index_ty.bits() < pointer_ty.bits());

    // Convert `index` to `addr_ty`.
    let extended_index = pos.ins().uextend(pointer_ty, index);

    // Add debug value-label alias so that debuginfo can name the extended
    // value as the address
    let loc = pos.srcloc();
    let loc = RelSourceLoc::from_base_offset(pos.func.params.base_srcloc(), loc);
    pos.func
        .stencil
        .dfg
        .add_value_label_alias(extended_index, loc, index);

    extended_index
}

struct SpectreOobComparison {
    cc: IntCC,
    lhs: ir::Value,
    rhs: ir::Value,
}

/// Emit code for the base address computation of a `heap_addr` instruction,
/// without any bounds checks (other than optional Spectre mitigations).
fn compute_addr(
    isa: &dyn TargetIsa,
    pos: &mut FuncCursor,
    heap: ir::Heap,
    addr_ty: ir::Type,
    index: ir::Value,
    offset: u32,
    // If we are performing Spectre mitigation with conditional selects, the
    // values to compare and the condition code that indicates an out-of bounds
    // condition; on this condition, the conditional move will choose a
    // speculatively safe address (a zero / null pointer) instead.
    spectre_oob_comparison: Option<SpectreOobComparison>,
) -> ir::Value {
    debug_assert_eq!(pos.func.dfg.value_type(index), addr_ty);

    // Add the heap base address base
    let base = if isa.flags().enable_pinned_reg() && isa.flags().use_pinned_reg_as_heap_base() {
        let base = pos.ins().get_pinned_reg(isa.pointer_type());
        trace!("  inserting: {}", pos.func.dfg.display_value_inst(base));
        base
    } else {
        let base_gv = pos.func.heaps[heap].base;
        let base = pos.ins().global_value(addr_ty, base_gv);
        trace!("  inserting: {}", pos.func.dfg.display_value_inst(base));
        base
    };

    if let Some(SpectreOobComparison { cc, lhs, rhs }) = spectre_oob_comparison {
        let final_base = pos.ins().iadd(base, index);
        // NB: The addition of the offset immediate must happen *before* the
        // `select_spectre_guard`. If it happens after, then we potentially are
        // letting speculative execution read the whole first 4GiB of memory.
        let final_addr = if offset == 0 {
            final_base
        } else {
            let final_addr = pos.ins().iadd_imm(final_base, offset as i64);
            trace!(
                "  inserting: {}",
                pos.func.dfg.display_value_inst(final_addr)
            );
            final_addr
        };
        let zero = pos.ins().iconst(addr_ty, 0);
        trace!("  inserting: {}", pos.func.dfg.display_value_inst(zero));

        let cmp = pos.ins().icmp(cc, lhs, rhs);
        trace!("  inserting: {}", pos.func.dfg.display_value_inst(cmp));

        let value = pos.ins().select_spectre_guard(cmp, zero, final_addr);
        trace!("  inserting: {}", pos.func.dfg.display_value_inst(value));
        value
    } else if offset == 0 {
        let addr = pos.ins().iadd(base, index);
        trace!("  inserting: {}", pos.func.dfg.display_value_inst(addr));
        addr
    } else {
        let final_base = pos.ins().iadd(base, index);
        trace!(
            "  inserting: {}",
            pos.func.dfg.display_value_inst(final_base)
        );
        let addr = pos.ins().iadd_imm(final_base, offset as i64);
        trace!("  inserting: {}", pos.func.dfg.display_value_inst(addr));
        addr
    }
}

Get the width of pointers on this ISA, in units of bits.

Examples found in repository?
src/isa/mod.rs (line 341)
340
341
342
    pub fn pointer_type(&self) -> ir::Type {
        ir::Type::int(self.pointer_bits() as u16).unwrap()
    }

Get the width of pointers on this ISA, in units of bytes.

Examples found in repository?
src/simple_preopt.rs (line 917)
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) {
    let _tt = timing::preopt();

    let mut pos = FuncCursor::new(func);
    let native_word_width = isa.pointer_bytes() as u32;
    let mut optimizer = simplify::peephole_optimizer(isa);

    while let Some(block) = pos.next_block() {
        while let Some(inst) = pos.next_inst() {
            simplify::apply_all(&mut optimizer, &mut pos, inst, native_word_width);

            // Try to transform divide-by-constant into simpler operations.
            if let Some(divrem_info) = get_div_info(inst, &pos.func.dfg) {
                do_divrem_transformation(&divrem_info, &mut pos, inst);
                continue;
            }

            branch_order(&mut pos, cfg, block, inst);
        }
    }
}

Get the information needed by frontends producing Cranelift IR.

Trait Implementations§

Formats the value using the given formatter. Read more
Converts to this type from the input type.

Implementors§