Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Relax verification to allow I8X16 to act as a default vector type #1236

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions cranelift-codegen/src/verifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::ir;
use crate::ir::entities::AnyEntity;
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint};
use crate::ir::types::I8X16;
use crate::ir::{
types, ArgumentLoc, Ebb, FuncRef, Function, GlobalValue, Inst, InstructionData, JumpTable,
Opcode, SigRef, StackSlot, StackSlotKind, Type, Value, ValueDef, ValueList, ValueLoc,
Expand Down Expand Up @@ -1189,6 +1190,12 @@ impl<'a> Verifier<'a> {
let arg_type = self.func.dfg.value_type(arg);
match constraints.value_argument_constraint(i, ctrl_type) {
ResolvedConstraint::Bound(expected_type) => {
// Skip checking of the V128 default type (i.e. I8X16): this relaxation of type
// checking allows incoming values of I8X16 (say from a function signature) to
// be used as any vector type of the same width.
if arg_type == I8X16 && expected_type.bits() == arg_type.bits() {
continue;
}
if arg_type != expected_type {
report!(
errors,
Expand Down Expand Up @@ -1412,6 +1419,15 @@ impl<'a> Verifier<'a> {
}
for (i, (&arg, &expected_type)) in args.iter().zip(expected_types).enumerate() {
let arg_type = self.func.dfg.value_type(arg);
// Skip checking of the V128 default type (i.e. I8X16): this relaxation of type
// checking is the inverse of the one in `typecheck_fixed_args` above. It allows
// values returned from a function to be any vector of the same width as the
// default type.
if expected_type.value_type == I8X16
&& arg_type.bits() == expected_type.value_type.bits()
{
continue;
}
if arg_type != expected_type.value_type {
report!(
errors,
Expand Down
39 changes: 39 additions & 0 deletions filetests/verifier/simd-default-type.clif
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
test verifier
set enable_simd
target x86_64

; This file tests using I8X16 as a default type to represent Wasm's V128 type. Values of Wasm's V128
; type can be used in any instruction of similar width (e.g. `i32x4.add`). Cranelift must build
; signatures for Wasm functions using V128 and, lacking a special type to do so, Cranelift
; uses I8X16 for V128. To avoid excessive bitcasting, however, we relax the verifier to allow
; I8X16 to type-check as the other 128-bit types. This implies that:
; - SIMD instructions must have explicit types (e.g. iadd.i32x4) to avoid type inference deciding
; a wrong type and emitting the wrong instruction
; - The verifier will be unable to catch unintended errors in sequences like: `v1 = iadd.i8x16 v0,
; v0; v2 = isub.i32x4 v1, v0` (presumably we want v0 and v1 to be I8X16 in fact and would want the
; verifier to tell us that we should not add them as I32X4s).

function %passes_non_default_type(i32x4) {
ebb0(v0: i32x4):
v1 = fneg.f32x4 v0 ; error: arg 0 (v0) has type i32x4, expected f32x4
return
}

function %passes_default_type(i8x16) {
ebb0(v0: i8x16):
v1 = fadd.f32x4 v0, v0
return
}

function %returns_non_default_type() -> i64x2 {
ebb0:
v1 = vconst.i32x4 [0 1 2 3]
return v1 ; error: arg 0 (v1) has type i32x4, must match function signature of i64x2
}

function %returns_default_type() -> i8x16 {
ebb0:
v1 = vconst.i32x4 [0 1 2 3]
return v1
}