Check for invalid special type constraints.

The extend and reduce instructions have additional type constraints.

Stop inserting sextend instructions after ctz, clz, and popcnt when
translating from WebAssembly. The Cretonne instructions have the same
signature as the WebAssembly equivalents.
This commit is contained in:
Jakob Stoklund Olesen
2017-09-28 16:22:04 -07:00
parent 2888ff5bf3
commit 53404a9387
3 changed files with 74 additions and 16 deletions

View File

@@ -95,3 +95,17 @@ function %jump_args2() {
ebb1(v10: i64, v11: i16): ebb1(v10: i64, v11: i16):
return return
} }
function %bad_extend() {
ebb0:
v0 = iconst.i32 10
v1 = uextend.i16 v0 ; error: input i32 must be smaller than output i16
return
}
function %bad_reduce() {
ebb0:
v0 = iconst.i32 10
v1 = ireduce.i64 v0 ; error: input i32 must be larger than output i64
return
}

View File

@@ -49,8 +49,6 @@
//! //!
//! - Stack slot loads and stores must be in-bounds. //! - Stack slot loads and stores must be in-bounds.
//! - Immediate constraints for certain opcodes, like `udiv_imm v3, 0`. //! - Immediate constraints for certain opcodes, like `udiv_imm v3, 0`.
//! - Extend / truncate instructions have more type constraints: Source type can't be
//! larger / smaller than result type.
//! - `Insertlane` and `extractlane` instructions have immediate lane numbers that must be in //! - `Insertlane` and `extractlane` instructions have immediate lane numbers that must be in
//! range for their polymorphic type. //! range for their polymorphic type.
//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number //! - Swizzle and shuffle instructions take a variable number of lane arguments. The number
@@ -574,6 +572,7 @@ impl<'a> Verifier<'a> {
self.typecheck_fixed_args(inst, ctrl_type)?; self.typecheck_fixed_args(inst, ctrl_type)?;
self.typecheck_variable_args(inst)?; self.typecheck_variable_args(inst)?;
self.typecheck_return(inst)?; self.typecheck_return(inst)?;
self.typecheck_special(inst, ctrl_type)?;
Ok(()) Ok(())
} }
@@ -818,6 +817,57 @@ impl<'a> Verifier<'a> {
Ok(()) Ok(())
} }
// Check special-purpose type constraints that can't be expressed in the normal opcode
// constraints.
fn typecheck_special(&self, inst: Inst, ctrl_type: Type) -> Result {
match self.func.dfg[inst] {
ir::InstructionData::Unary { opcode, arg } => {
let arg_type = self.func.dfg.value_type(arg);
match opcode {
Opcode::Bextend | Opcode::Uextend | Opcode::Sextend | Opcode::Fpromote => {
if arg_type.lane_count() != ctrl_type.lane_count() {
return err!(
inst,
"input {} and output {} must have same number of lanes",
arg_type,
ctrl_type
);
}
if arg_type.lane_bits() >= ctrl_type.lane_bits() {
return err!(
inst,
"input {} must be smaller than output {}",
arg_type,
ctrl_type
);
}
}
Opcode::Breduce | Opcode::Ireduce | Opcode::Fdemote => {
if arg_type.lane_count() != ctrl_type.lane_count() {
return err!(
inst,
"input {} and output {} must have same number of lanes",
arg_type,
ctrl_type
);
}
if arg_type.lane_bits() <= ctrl_type.lane_bits() {
return err!(
inst,
"input {} must be larger than output {}",
arg_type,
ctrl_type
);
}
}
_ => {}
}
}
_ => {}
}
Ok(())
}
fn cfg_integrity(&self, cfg: &ControlFlowGraph) -> Result { fn cfg_integrity(&self, cfg: &ControlFlowGraph) -> Result {
let mut expected_succs = BTreeSet::<Ebb>::new(); let mut expected_succs = BTreeSet::<Ebb>::new();
let mut got_succs = BTreeSet::<Ebb>::new(); let mut got_succs = BTreeSet::<Ebb>::new();

View File

@@ -486,33 +486,27 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
/******************************* Unary Operators *************************************/ /******************************* Unary Operators *************************************/
Operator::I32Clz => { Operator::I32Clz => {
let arg = state.pop1(); let arg = state.pop1();
let val = builder.ins().clz(arg); state.push1(builder.ins().clz(arg));
state.push1(builder.ins().sextend(I32, val));
} }
Operator::I64Clz => { Operator::I64Clz => {
let arg = state.pop1(); let arg = state.pop1();
let val = builder.ins().clz(arg); state.push1(builder.ins().clz(arg));
state.push1(builder.ins().sextend(I64, val));
} }
Operator::I32Ctz => { Operator::I32Ctz => {
let val = state.pop1(); let arg = state.pop1();
let short_res = builder.ins().ctz(val); state.push1(builder.ins().ctz(arg));
state.push1(builder.ins().sextend(I32, short_res));
} }
Operator::I64Ctz => { Operator::I64Ctz => {
let val = state.pop1(); let arg = state.pop1();
let short_res = builder.ins().ctz(val); state.push1(builder.ins().ctz(arg));
state.push1(builder.ins().sextend(I64, short_res));
} }
Operator::I32Popcnt => { Operator::I32Popcnt => {
let arg = state.pop1(); let arg = state.pop1();
let val = builder.ins().popcnt(arg); state.push1(builder.ins().popcnt(arg));
state.push1(builder.ins().sextend(I32, val));
} }
Operator::I64Popcnt => { Operator::I64Popcnt => {
let arg = state.pop1(); let arg = state.pop1();
let val = builder.ins().popcnt(arg); state.push1(builder.ins().popcnt(arg));
state.push1(builder.ins().sextend(I64, val));
} }
Operator::I64ExtendSI32 => { Operator::I64ExtendSI32 => {
let val = state.pop1(); let val = state.pop1();