diff --git a/lib/cretonne/meta/gen_instr.py b/lib/cretonne/meta/gen_instr.py index d05bef96df..67ab23bda9 100644 --- a/lib/cretonne/meta/gen_instr.py +++ b/lib/cretonne/meta/gen_instr.py @@ -417,10 +417,20 @@ def gen_type_constraints(fmt, instrs): get_constraint(i.ins[idx], ctrl_typevar, type_sets)) offset = operand_seqs.add(constraints) fixed_results = len(i.value_results) + # Can the controlling type variable be inferred from the designated + # operand? use_typevar_operand = i.is_polymorphic and i.use_typevar_operand + # Can the controlling type variable be inferred from the result? + use_result = (fixed_results > 0 and + i.outs[i.value_results[0]].typevar != ctrl_typevar) + # Are we required to use the designated operand instead of the + # result? + requires_typevar_operand = use_typevar_operand and not use_result fmt.comment( - '{}: fixed_results={}, use_typevar_operand={}' - .format(i.camel_name, fixed_results, use_typevar_operand)) + ('{}: fixed_results={}, use_typevar_operand={}, ' + + 'requires_typevar_operand={}') + .format(i.camel_name, fixed_results, use_typevar_operand, + requires_typevar_operand)) fmt.comment('Constraints={}'.format(constraints)) if i.is_polymorphic: fmt.comment( @@ -430,6 +440,8 @@ def gen_type_constraints(fmt, instrs): flags = fixed_results if use_typevar_operand: flags |= 8 + if requires_typevar_operand: + flags |= 0x10 with fmt.indented('OpcodeConstraints {', '},'): fmt.line('flags: {:#04x},'.format(flags)) diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index d778ddb9a3..95311d48d8 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -14,6 +14,7 @@ use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, ImmVector}; use ir::condcodes::*; use ir::types; +use ir::DataFlowGraph; use ref_slice::*; use packed_option::PackedOption; @@ -492,6 +493,27 @@ impl InstructionData { _ => CallInfo::NotACall, } } + + /// Get the controlling type variable, or `VOID` if this instruction isn't polymorphic. + /// + /// In most cases, the controlling type variable is the same as the first result type, but some + /// opcodes require us to read the type of the designated type variable operand from `dfg`. + pub fn ctrl_typevar(&self, dfg: &DataFlowGraph) -> Type { + let constraints = self.opcode().constraints(); + + if !constraints.is_polymorphic() { + types::VOID + } else if constraints.requires_typevar_operand() { + // Not all instruction formats have a designated operand, but in that case + // `requires_typevar_operand()` should never be true. + dfg.value_type(self.typevar_operand() + .expect("Instruction format doesn't have a designated operand, bad opcode.")) + } else { + // For locality of reference, we prefer to get the controlling type variable from + // `idata` itself, when possible. + self.first_type() + } + } } /// Information about branch and jump instructions. @@ -537,8 +559,12 @@ pub struct OpcodeConstraints { /// Bit 3: /// This opcode is polymorphic and the controlling type variable can be inferred from the /// designated input operand. This is the `typevar_operand` index given to the - /// `InstructionFormat` meta language object. When bit 0 is not set, the controlling type - /// variable must be the first output value instead. + /// `InstructionFormat` meta language object. When this bit is not set, the controlling + /// type variable must be the first output value instead. + /// + /// Bit 4: + /// This opcode is polymorphic and the controlling type variable does *not* appear as the + /// first result type. flags: u8, /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`. @@ -559,6 +585,16 @@ impl OpcodeConstraints { (self.flags & 0x8) != 0 } + /// Is it necessary to look at the designated value input operand in order to determine the + /// controlling type variable, or is it good enough to use the first return type? + /// + /// Most polymorphic instructions produce a single result with the type of the controlling type + /// variable. A few polymorphic instructions either don't produce any results, or produce + /// results with a fixed type. These instructions return `true`. + pub fn requires_typevar_operand(self) -> bool { + (self.flags & 0x10) != 0 + } + /// Get the number of *fixed* result values produced by this opcode. /// This does not include `variable_args` produced by calls. pub fn fixed_results(self) -> usize { diff --git a/lib/cretonne/src/isa/arm32/mod.rs b/lib/cretonne/src/isa/arm32/mod.rs index 63e9516a13..9af02883e3 100644 --- a/lib/cretonne/src/isa/arm32/mod.rs +++ b/lib/cretonne/src/isa/arm32/mod.rs @@ -53,8 +53,8 @@ impl TargetIsa for Isa { registers::INFO.clone() } - fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Result { - lookup_enclist(inst.first_type(), + fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { + lookup_enclist(inst.ctrl_typevar(dfg), inst.opcode(), self.cpumode, &enc_tables::LEVEL2[..]) diff --git a/lib/cretonne/src/isa/arm64/mod.rs b/lib/cretonne/src/isa/arm64/mod.rs index 4d00b1d49e..50f54524a9 100644 --- a/lib/cretonne/src/isa/arm64/mod.rs +++ b/lib/cretonne/src/isa/arm64/mod.rs @@ -46,8 +46,8 @@ impl TargetIsa for Isa { registers::INFO.clone() } - fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Result { - lookup_enclist(inst.first_type(), + fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { + lookup_enclist(inst.ctrl_typevar(dfg), inst.opcode(), &enc_tables::LEVEL1_A64[..], &enc_tables::LEVEL2[..]) diff --git a/lib/cretonne/src/isa/intel/mod.rs b/lib/cretonne/src/isa/intel/mod.rs index 533c264e3d..e5c99521e3 100644 --- a/lib/cretonne/src/isa/intel/mod.rs +++ b/lib/cretonne/src/isa/intel/mod.rs @@ -53,8 +53,8 @@ impl TargetIsa for Isa { registers::INFO.clone() } - fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Result { - lookup_enclist(inst.first_type(), + fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { + lookup_enclist(inst.ctrl_typevar(dfg), inst.opcode(), self.cpumode, &enc_tables::LEVEL2[..]) diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index b997316733..8b27a46119 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -53,8 +53,8 @@ impl TargetIsa for Isa { registers::INFO.clone() } - fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Result { - lookup_enclist(inst.first_type(), + fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { + lookup_enclist(inst.ctrl_typevar(dfg), inst.opcode(), self.cpumode, &enc_tables::LEVEL2[..])