Remove IFLAGS/FFLAGS types (#5406)
All instructions using the CPU flags types (IFLAGS/FFLAGS) were already removed. This patch completes the cleanup by removing all remaining instructions that define values of CPU flags types, as well as the types themselves. Specifically, the following features are removed: - The IFLAGS and FFLAGS types and the SpecialType category. - Special handling of IFLAGS and FFLAGS in machinst/isle.rs and machinst/lower.rs. - The ifcmp, ifcmp_imm, ffcmp, iadd_ifcin, iadd_ifcout, iadd_ifcarry, isub_ifbin, isub_ifbout, and isub_ifborrow instructions. - The writes_cpu_flags instruction property. - The flags verifier pass. - Flags handling in the interpreter. All of these features are currently unused; no functional change intended by this patch. This addresses https://github.com/bytecodealliance/wasmtime/issues/3249.
This commit is contained in:
@@ -1511,6 +1511,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn aliases() {
|
||||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::InstBuilder;
|
||||
|
||||
let mut func = Function::new();
|
||||
@@ -1525,7 +1526,7 @@ mod tests {
|
||||
assert_eq!(pos.func.dfg.resolve_aliases(v1), v1);
|
||||
|
||||
let arg0 = pos.func.dfg.append_block_param(block0, types::I32);
|
||||
let (s, c) = pos.ins().iadd_ifcout(v1, arg0);
|
||||
let (s, c) = pos.ins().iadd_cout(v1, arg0);
|
||||
let iadd = match pos.func.dfg.value_def(s) {
|
||||
ValueDef::Result(i, 0) => i,
|
||||
_ => panic!(),
|
||||
@@ -1535,9 +1536,9 @@ mod tests {
|
||||
pos.func.dfg.clear_results(iadd);
|
||||
pos.func.dfg.attach_result(iadd, s);
|
||||
|
||||
// Replace `iadd_ifcout` with a normal `iadd` and an `ifcmp`.
|
||||
// Replace `iadd_cout` with a normal `iadd` and an `icmp`.
|
||||
pos.func.dfg.replace(iadd).iadd(v1, arg0);
|
||||
let c2 = pos.ins().ifcmp(s, v1);
|
||||
let c2 = pos.ins().icmp(IntCC::Equal, s, v1);
|
||||
pos.func.dfg.change_to_alias(c, c2);
|
||||
|
||||
assert_eq!(pos.func.dfg.resolve_aliases(c2), c2);
|
||||
|
||||
@@ -244,14 +244,6 @@ impl Type {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this a CPU flags type?
|
||||
pub fn is_flags(self) -> bool {
|
||||
match self {
|
||||
IFLAGS | FFLAGS => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this a ref type?
|
||||
pub fn is_ref(self) -> bool {
|
||||
match self {
|
||||
@@ -453,12 +445,10 @@ impl Display for Type {
|
||||
} else if self.is_ref() {
|
||||
write!(f, "r{}", self.lane_bits())
|
||||
} else {
|
||||
f.write_str(match *self {
|
||||
IFLAGS => "iflags",
|
||||
FFLAGS => "fflags",
|
||||
match *self {
|
||||
INVALID => panic!("INVALID encountered"),
|
||||
_ => panic!("Unknown Type(0x{:x})", self.0),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,8 +468,6 @@ impl Debug for Type {
|
||||
} else {
|
||||
match *self {
|
||||
INVALID => write!(f, "types::INVALID"),
|
||||
IFLAGS => write!(f, "types::IFLAGS"),
|
||||
FFLAGS => write!(f, "types::FFLAGS"),
|
||||
_ => write!(f, "Type(0x{:x})", self.0),
|
||||
}
|
||||
}
|
||||
@@ -501,10 +489,6 @@ mod tests {
|
||||
fn basic_scalars() {
|
||||
assert_eq!(INVALID, INVALID.lane_type());
|
||||
assert_eq!(0, INVALID.bits());
|
||||
assert_eq!(IFLAGS, IFLAGS.lane_type());
|
||||
assert_eq!(0, IFLAGS.bits());
|
||||
assert_eq!(FFLAGS, FFLAGS.lane_type());
|
||||
assert_eq!(0, FFLAGS.bits());
|
||||
assert_eq!(I8, I8.lane_type());
|
||||
assert_eq!(I16, I16.lane_type());
|
||||
assert_eq!(I32, I32.lane_type());
|
||||
@@ -518,8 +502,6 @@ mod tests {
|
||||
assert_eq!(R64, R64.lane_type());
|
||||
|
||||
assert_eq!(INVALID.lane_bits(), 0);
|
||||
assert_eq!(IFLAGS.lane_bits(), 0);
|
||||
assert_eq!(FFLAGS.lane_bits(), 0);
|
||||
assert_eq!(I8.lane_bits(), 8);
|
||||
assert_eq!(I16.lane_bits(), 16);
|
||||
assert_eq!(I32.lane_bits(), 32);
|
||||
@@ -535,7 +517,6 @@ mod tests {
|
||||
fn typevar_functions() {
|
||||
assert_eq!(INVALID.half_width(), None);
|
||||
assert_eq!(INVALID.half_width(), None);
|
||||
assert_eq!(FFLAGS.half_width(), None);
|
||||
assert_eq!(I8.half_width(), None);
|
||||
assert_eq!(I16.half_width(), Some(I8));
|
||||
assert_eq!(I32.half_width(), Some(I16));
|
||||
@@ -546,8 +527,6 @@ mod tests {
|
||||
assert_eq!(F64.half_width(), Some(F32));
|
||||
|
||||
assert_eq!(INVALID.double_width(), None);
|
||||
assert_eq!(IFLAGS.double_width(), None);
|
||||
assert_eq!(FFLAGS.double_width(), None);
|
||||
assert_eq!(I8.double_width(), Some(I16));
|
||||
assert_eq!(I16.double_width(), Some(I32));
|
||||
assert_eq!(I32.double_width(), Some(I64));
|
||||
@@ -614,8 +593,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn format_scalars() {
|
||||
assert_eq!(IFLAGS.to_string(), "iflags");
|
||||
assert_eq!(FFLAGS.to_string(), "fflags");
|
||||
assert_eq!(I8.to_string(), "i8");
|
||||
assert_eq!(I16.to_string(), "i16");
|
||||
assert_eq!(I32.to_string(), "i32");
|
||||
|
||||
@@ -1940,14 +1940,6 @@
|
||||
(MInst.AluRRR (ALUOp.AddS) (operand_size ty) dst src1 src2)
|
||||
dst)))
|
||||
|
||||
;; Helper for emitting `adds` instructions, setting flags in ambient
|
||||
;; state. Used only for `iadd_ifcout`.
|
||||
(decl add_with_flags (Type Reg Reg) Reg)
|
||||
(rule (add_with_flags ty src1 src2)
|
||||
(let ((dst WritableReg (temp_writable_reg $I64))
|
||||
(_ Unit (emit (MInst.AluRRR (ALUOp.AddS) (operand_size ty) dst src1 src2))))
|
||||
dst))
|
||||
|
||||
;; Helper for emitting `adc` instructions.
|
||||
(decl adc_paired (Type Reg Reg) ConsumesFlags)
|
||||
(rule (adc_paired ty src1 src2)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! This module defines aarch64-specific machine instruction types.
|
||||
|
||||
use crate::binemit::{Addend, CodeOffset, Reloc};
|
||||
use crate::ir::types::{F32, F64, FFLAGS, I128, I16, I32, I64, I8, I8X16, IFLAGS, R32, R64};
|
||||
use crate::ir::types::{F32, F64, I128, I16, I32, I64, I8, I8X16, R32, R64};
|
||||
use crate::ir::{types, ExternalName, MemFlags, Opcode, Type};
|
||||
use crate::isa::CallConv;
|
||||
use crate::machinst::*;
|
||||
@@ -1280,7 +1280,6 @@ impl MachInst for Inst {
|
||||
Ok((&[RegClass::Float], &[I8X16]))
|
||||
}
|
||||
_ if ty.is_dynamic_vector() => Ok((&[RegClass::Float], &[I8X16])),
|
||||
IFLAGS | FFLAGS => Ok((&[RegClass::Int], &[I64])),
|
||||
_ => Err(CodegenError::Unsupported(format!(
|
||||
"Unexpected SSA-value type: {}",
|
||||
ty
|
||||
|
||||
@@ -2332,36 +2332,6 @@
|
||||
(lower_msb Reg (lsr_imm $I64 lower_msb (imm_shift_from_u8 63))))
|
||||
(add_shift $I64 lower_msb upper_msb (lshl_from_u64 $I64 1))))
|
||||
|
||||
;;; Rules for `iadd_ifcout` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; This is a two-output instruction that is needed for the
|
||||
;; legalizer's explicit heap-check sequence, among possible other
|
||||
;; uses. Its second output is a flags output only ever meant to
|
||||
;; check for overflow using the
|
||||
;; `backend.unsigned_add_overflow_condition()` condition.
|
||||
;;
|
||||
;; Note that the CLIF validation will ensure that no flag-setting
|
||||
;; operation comes between this IaddIfcout and its use (e.g., a
|
||||
;; Trapif). Thus, we can rely on implicit communication through the
|
||||
;; processor flags rather than explicitly generating flags into a
|
||||
;; register. We simply use the variant of the add instruction that
|
||||
;; sets flags (`adds`) here.
|
||||
;;
|
||||
;; Note that the second output (the flags) need not be generated,
|
||||
;; because flags are never materialized into a register; the only
|
||||
;; instructions that can use a value of type `iflags` or `fflags`
|
||||
;; will look directly for the flags-producing instruction (which can
|
||||
;; always be found, by construction) and merge it.
|
||||
;;
|
||||
;; Now handle the iadd as above, except use an AddS opcode that sets
|
||||
;; flags.
|
||||
|
||||
(rule (lower (has_type (ty_int ty)
|
||||
(iadd_ifcout a b)))
|
||||
(output_pair
|
||||
(add_with_flags ty a b)
|
||||
(invalid_reg)))
|
||||
|
||||
;;; Rules for `iadd_cout` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; For values smaller than a register, we do a normal `add` with both arguments
|
||||
|
||||
@@ -167,14 +167,6 @@ pub(crate) fn lower_insn_to_regs(
|
||||
|
||||
Opcode::Return => implemented_in_isle(ctx),
|
||||
|
||||
Opcode::Ifcmp | Opcode::Ffcmp => {
|
||||
// An Ifcmp/Ffcmp must always be seen as a use of a brif/brff or trueif/trueff
|
||||
// instruction. This will always be the case as long as the IR uses an Ifcmp/Ffcmp from
|
||||
// the same block, or a dominating block. In other words, it cannot pass through a BB
|
||||
// param (phi). The flags pass of the verifier will ensure this.
|
||||
panic!("Should never reach ifcmp as isel root!");
|
||||
}
|
||||
|
||||
Opcode::Icmp => implemented_in_isle(ctx),
|
||||
|
||||
Opcode::Fcmp => implemented_in_isle(ctx),
|
||||
@@ -253,8 +245,6 @@ pub(crate) fn lower_insn_to_regs(
|
||||
|
||||
Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => implemented_in_isle(ctx),
|
||||
|
||||
Opcode::IaddIfcout => implemented_in_isle(ctx),
|
||||
|
||||
Opcode::UaddOverflowTrap => implemented_in_isle(ctx),
|
||||
|
||||
Opcode::IaddCout => implemented_in_isle(ctx),
|
||||
@@ -267,15 +257,10 @@ pub(crate) fn lower_insn_to_regs(
|
||||
| Opcode::SremImm
|
||||
| Opcode::IrsubImm
|
||||
| Opcode::IaddCin
|
||||
| Opcode::IaddIfcin
|
||||
| Opcode::IaddCarry
|
||||
| Opcode::IaddIfcarry
|
||||
| Opcode::IsubBin
|
||||
| Opcode::IsubIfbin
|
||||
| Opcode::IsubBout
|
||||
| Opcode::IsubIfbout
|
||||
| Opcode::IsubBorrow
|
||||
| Opcode::IsubIfborrow
|
||||
| Opcode::BandImm
|
||||
| Opcode::BorImm
|
||||
| Opcode::BxorImm
|
||||
@@ -284,8 +269,7 @@ pub(crate) fn lower_insn_to_regs(
|
||||
| Opcode::IshlImm
|
||||
| Opcode::UshrImm
|
||||
| Opcode::SshrImm
|
||||
| Opcode::IcmpImm
|
||||
| Opcode::IfcmpImm => {
|
||||
| Opcode::IcmpImm => {
|
||||
panic!("ALU+imm and ALU+carry ops should not appear here!");
|
||||
}
|
||||
|
||||
|
||||
@@ -2037,10 +2037,6 @@
|
||||
(a2 Reg (alu_rr_imm12 (AluOPRRI.Addi) a (imm12_const 1))))
|
||||
(gen_select_reg (IntCC.SignedLessThan) r (zero_reg) a2 r)))
|
||||
|
||||
(decl output_ifcout (Reg) InstOutput)
|
||||
(rule (output_ifcout reg)
|
||||
(output_pair reg (value_regs_invalid)))
|
||||
|
||||
(decl gen_trapff (FloatCC Reg Reg Type TrapCode) InstOutput)
|
||||
(rule
|
||||
(gen_trapff cc a b ty trap_code)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use crate::binemit::{Addend, CodeOffset, Reloc};
|
||||
pub use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::types::{F32, F64, FFLAGS, I128, I16, I32, I64, I8, IFLAGS, R32, R64};
|
||||
use crate::ir::types::{F32, F64, I128, I16, I32, I64, I8, R32, R64};
|
||||
|
||||
pub use crate::ir::{ExternalName, MemFlags, Opcode, SourceLoc, Type, ValueLabel};
|
||||
use crate::isa::CallConv;
|
||||
@@ -778,8 +778,6 @@ impl MachInst for Inst {
|
||||
F32 => Ok((&[RegClass::Float], &[F32])),
|
||||
F64 => Ok((&[RegClass::Float], &[F64])),
|
||||
I128 => Ok((&[RegClass::Int, RegClass::Int], &[I64, I64])),
|
||||
IFLAGS => Ok((&[RegClass::Int], &[IFLAGS])),
|
||||
FFLAGS => Ok((&[RegClass::Int], &[FFLAGS])),
|
||||
_ => Err(CodegenError::Unsupported(format!(
|
||||
"Unexpected SSA-value type: {}",
|
||||
ty
|
||||
|
||||
@@ -43,11 +43,6 @@
|
||||
(high Reg (alu_add high_tmp carry)))
|
||||
(value_regs low high)))
|
||||
|
||||
;;; Rules for `iadd_ifcout` ;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type (fits_in_64 ty) (iadd_ifcout x y)))
|
||||
(output_ifcout (alu_add x y)))
|
||||
|
||||
;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;;
|
||||
(rule
|
||||
(lower (has_type (fits_in_64 ty) (uadd_overflow_trap x y tc)))
|
||||
@@ -747,14 +742,6 @@
|
||||
(rule
|
||||
(lower (icmp cc x @ (value_type ty) y))
|
||||
(lower_icmp cc x y ty))
|
||||
;; special for `iadd_ifcout` first out.
|
||||
(rule 2
|
||||
(lower (icmp cc (iadd_ifcout a @ (value_type ty) b) y))
|
||||
(lower_icmp cc (alu_add a b) y ty))
|
||||
|
||||
(rule 1
|
||||
(lower (icmp cc x (iadd_ifcout a @ (value_type ty) b)))
|
||||
(lower_icmp cc x (alu_add a b) ty))
|
||||
|
||||
(decl gen_fcmp (FloatCC Value Value Type) Reg)
|
||||
(rule
|
||||
|
||||
@@ -1244,9 +1244,6 @@ impl MachInst for Inst {
|
||||
types::F64 => Ok((&[RegClass::Float], &[types::F64])),
|
||||
types::I128 => Ok((&[RegClass::Float], &[types::I128])),
|
||||
_ if ty.is_vector() && ty.bits() == 128 => Ok((&[RegClass::Float], &[types::I8X16])),
|
||||
// FIXME: We don't really have IFLAGS, but need to allow it here
|
||||
// for now to support the SelectifSpectreGuard instruction.
|
||||
types::IFLAGS => Ok((&[RegClass::Int], &[types::I64])),
|
||||
_ => Err(CodegenError::Unsupported(format!(
|
||||
"Unexpected SSA-value type: {}",
|
||||
ty
|
||||
|
||||
@@ -192,47 +192,6 @@
|
||||
(vec_unpacks_low ty y))))
|
||||
|
||||
|
||||
;;;; Rules for `iadd_ifcout` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; N.B.: the second output of `iadd_ifcout` is meant to be the `iflags` value
|
||||
;; containing the carry result, but we do not support the `iflags` mechanism.
|
||||
;; However, the only actual use case is where `iadd_ifcout` feeds into `trapif`,
|
||||
;; which is implemented by explicitly matching on the flags producer. So we can
|
||||
;; get away with just using an invalid second output, and the reg-renaming code
|
||||
;; does the right thing, for now.
|
||||
(decl output_ifcout (Reg) InstOutput)
|
||||
(rule (output_ifcout reg)
|
||||
(output_pair reg (value_regs_invalid)))
|
||||
|
||||
;; Add two registers.
|
||||
(rule 0 (lower (has_type (fits_in_64 ty) (iadd_ifcout x y)))
|
||||
(output_ifcout (add_logical_reg ty x y)))
|
||||
|
||||
;; Add a register and a zero-extended register.
|
||||
(rule 4 (lower (has_type (fits_in_64 ty) (iadd_ifcout x (zext32_value y))))
|
||||
(output_ifcout (add_logical_reg_zext32 ty x y)))
|
||||
(rule 8 (lower (has_type (fits_in_64 ty) (iadd_ifcout (zext32_value x) y)))
|
||||
(output_ifcout (add_logical_reg_zext32 ty y x)))
|
||||
|
||||
;; Add a register and an immediate.
|
||||
(rule 3 (lower (has_type (fits_in_64 ty) (iadd_ifcout x (u32_from_value y))))
|
||||
(output_ifcout (add_logical_zimm32 ty x y)))
|
||||
(rule 7 (lower (has_type (fits_in_64 ty) (iadd_ifcout (u32_from_value x) y)))
|
||||
(output_ifcout (add_logical_zimm32 ty y x)))
|
||||
|
||||
;; Add a register and memory (32/64-bit types).
|
||||
(rule 2 (lower (has_type (fits_in_64 ty) (iadd_ifcout x (sinkable_load_32_64 y))))
|
||||
(output_ifcout (add_logical_mem ty x (sink_load y))))
|
||||
(rule 6 (lower (has_type (fits_in_64 ty) (iadd_ifcout (sinkable_load_32_64 x) y)))
|
||||
(output_ifcout (add_logical_mem ty y (sink_load x))))
|
||||
|
||||
;; Add a register and zero-extended memory.
|
||||
(rule 1 (lower (has_type (fits_in_64 ty) (iadd_ifcout x (sinkable_uload32 y))))
|
||||
(output_ifcout (add_logical_mem_zext32 ty x (sink_uload32 y))))
|
||||
(rule 5 (lower (has_type (fits_in_64 ty) (iadd_ifcout (sinkable_uload32 x) y)))
|
||||
(output_ifcout (add_logical_mem_zext32 ty y (sink_uload32 x))))
|
||||
|
||||
|
||||
;;;; Rules for `iabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Absolute value of a register.
|
||||
@@ -3742,14 +3701,12 @@
|
||||
|
||||
;;;; Rules for `select_spectre_guard` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; We do not support the `iflags` mechanism on our platform. However, common
|
||||
;; code will unconditionally emit certain patterns using `iflags` which we
|
||||
;; need to handle somehow. Note that only those specific patterns are
|
||||
;; recognized by the code below, other uses will fail to lower.
|
||||
|
||||
;; We need to guarantee a conditional move instruction. But on this platform
|
||||
;; this is already the best way to implement select in general, so the
|
||||
;; implementation of `select_spectre_guard` is identical to `select`.
|
||||
(rule (lower (has_type ty (select_spectre_guard
|
||||
(icmp int_cc x y) val_true val_false)))
|
||||
(select_bool_reg ty (icmp_val $false int_cc x y)
|
||||
val_cond val_true val_false)))
|
||||
(select_bool_reg ty (value_nonzero val_cond)
|
||||
(put_in_reg val_true) (put_in_reg val_false)))
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ impl LowerBackend for S390xBackend {
|
||||
| Opcode::Isplit
|
||||
| Opcode::Iconcat
|
||||
| Opcode::Iadd
|
||||
| Opcode::IaddIfcout
|
||||
| Opcode::Isub
|
||||
| Opcode::UaddSat
|
||||
| Opcode::SaddSat
|
||||
@@ -177,6 +176,7 @@ impl LowerBackend for S390xBackend {
|
||||
| Opcode::Trapnz
|
||||
| Opcode::ResumableTrapnz
|
||||
| Opcode::Debugtrap
|
||||
| Opcode::UaddOverflowTrap
|
||||
| Opcode::Call
|
||||
| Opcode::CallIndirect
|
||||
| Opcode::Return
|
||||
@@ -221,9 +221,6 @@ impl LowerBackend for S390xBackend {
|
||||
Opcode::GlobalValue => {
|
||||
panic!("global_value should have been removed by legalization!");
|
||||
}
|
||||
Opcode::Ifcmp | Opcode::Ffcmp => {
|
||||
panic!("Flags opcode should not be encountered.");
|
||||
}
|
||||
Opcode::Jump | Opcode::Brz | Opcode::Brnz | Opcode::BrTable => {
|
||||
panic!("Branch opcode reached non-branch lowering logic!");
|
||||
}
|
||||
@@ -235,17 +232,11 @@ impl LowerBackend for S390xBackend {
|
||||
| Opcode::SremImm
|
||||
| Opcode::IrsubImm
|
||||
| Opcode::IaddCin
|
||||
| Opcode::IaddIfcin
|
||||
| Opcode::IaddCout
|
||||
| Opcode::IaddCarry
|
||||
| Opcode::IaddIfcarry
|
||||
| Opcode::UaddOverflowTrap
|
||||
| Opcode::IsubBin
|
||||
| Opcode::IsubIfbin
|
||||
| Opcode::IsubBout
|
||||
| Opcode::IsubIfbout
|
||||
| Opcode::IsubBorrow
|
||||
| Opcode::IsubIfborrow
|
||||
| Opcode::BandImm
|
||||
| Opcode::BorImm
|
||||
| Opcode::BxorImm
|
||||
@@ -254,8 +245,7 @@ impl LowerBackend for S390xBackend {
|
||||
| Opcode::IshlImm
|
||||
| Opcode::UshrImm
|
||||
| Opcode::SshrImm
|
||||
| Opcode::IcmpImm
|
||||
| Opcode::IfcmpImm => {
|
||||
| Opcode::IcmpImm => {
|
||||
panic!("ALU+imm and ALU+carry ops should not appear here!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2318,7 +2318,6 @@ impl MachInst for Inst {
|
||||
assert!(ty.bits() <= 128);
|
||||
Ok((&[RegClass::Float], &[types::I8X16]))
|
||||
}
|
||||
types::IFLAGS | types::FFLAGS => Ok((&[RegClass::Int], &[types::I64])),
|
||||
_ => Err(CodegenError::Unsupported(format!(
|
||||
"Unexpected SSA-value type: {}",
|
||||
ty
|
||||
|
||||
@@ -135,49 +135,6 @@
|
||||
(uadd_sat x y)))
|
||||
(x64_paddusw x y))
|
||||
|
||||
;;;; Rules for `iadd_ifcout` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; N.B.: the second output of `iadd_ifcout` is meant to be the
|
||||
;; `iflags` value containing the carry result. However, we plan to
|
||||
;; replace this with a bool carry flag, and all consumers of `iflags`
|
||||
;; remain in the handwritten pattern-matching code and explicitly
|
||||
;; match on the flags producer. So we can get away with just
|
||||
;; using an invalid second output, and the reg-renaming code does the
|
||||
;; right thing, for now. For safety, we assert elsewhere that no one
|
||||
;; actually uses the register assigned to the SSA `iflags`-typed
|
||||
;; `Value`.
|
||||
|
||||
(decl output_ifcout (Reg) InstOutput)
|
||||
(rule (output_ifcout reg)
|
||||
(output_pair reg (value_regs_invalid)))
|
||||
|
||||
;; Add two registers.
|
||||
(rule 0 (lower (has_type (fits_in_64 ty)
|
||||
(iadd_ifcout x y)))
|
||||
(output_ifcout (x64_add ty x y)))
|
||||
|
||||
;; Add a register and an immediate.
|
||||
|
||||
(rule 1 (lower (has_type (fits_in_64 ty)
|
||||
(iadd_ifcout x (simm32_from_value y))))
|
||||
(output_ifcout (x64_add ty x y)))
|
||||
|
||||
(rule 2 (lower (has_type (fits_in_64 ty)
|
||||
(iadd_ifcout (simm32_from_value x) y)))
|
||||
(output_ifcout (x64_add ty y x)))
|
||||
|
||||
;; Add a register and memory.
|
||||
|
||||
(rule 3 (lower (has_type (fits_in_64 ty)
|
||||
(iadd_ifcout x (sinkable_load y))))
|
||||
(output_ifcout (x64_add ty x (sink_load_to_gpr_mem_imm y))))
|
||||
|
||||
(rule 4 (lower (has_type (fits_in_64 ty)
|
||||
(iadd_ifcout (sinkable_load x) y)))
|
||||
(output_ifcout (x64_add ty y (sink_load_to_gpr_mem_imm x))))
|
||||
|
||||
;; (No `iadd_ifcout` for `i128`.)
|
||||
|
||||
;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; `i64` and smaller.
|
||||
@@ -2180,9 +2137,6 @@
|
||||
(rule (lower (has_type $I64
|
||||
(uextend src @ (has_type $I32 (iadd _ _)))))
|
||||
src)
|
||||
(rule (lower (has_type $I64
|
||||
(uextend src @ (has_type $I32 (iadd_ifcout _ _)))))
|
||||
src)
|
||||
(rule (lower (has_type $I64
|
||||
(uextend src @ (has_type $I32 (isub _ _)))))
|
||||
src)
|
||||
|
||||
@@ -332,7 +332,6 @@ fn lower_insn_to_regs(
|
||||
| Opcode::Null
|
||||
| Opcode::Iadd
|
||||
| Opcode::IaddCout
|
||||
| Opcode::IaddIfcout
|
||||
| Opcode::SaddSat
|
||||
| Opcode::UaddSat
|
||||
| Opcode::Isub
|
||||
@@ -360,6 +359,7 @@ fn lower_insn_to_regs(
|
||||
| Opcode::Ineg
|
||||
| Opcode::Trap
|
||||
| Opcode::ResumableTrap
|
||||
| Opcode::UaddOverflowTrap
|
||||
| Opcode::Clz
|
||||
| Opcode::Ctz
|
||||
| Opcode::Popcnt
|
||||
@@ -500,13 +500,6 @@ fn lower_insn_to_regs(
|
||||
unimplemented!("Vector split/concat ops not implemented.");
|
||||
}
|
||||
|
||||
// Opcodes that should be removed by legalization. These should
|
||||
// eventually be removed if/when we replace in-situ legalization with
|
||||
// something better.
|
||||
Opcode::Ifcmp | Opcode::Ffcmp => {
|
||||
panic!("Should never reach ifcmp/ffcmp as isel root!");
|
||||
}
|
||||
|
||||
Opcode::IaddImm
|
||||
| Opcode::ImulImm
|
||||
| Opcode::UdivImm
|
||||
@@ -515,16 +508,10 @@ fn lower_insn_to_regs(
|
||||
| Opcode::SremImm
|
||||
| Opcode::IrsubImm
|
||||
| Opcode::IaddCin
|
||||
| Opcode::IaddIfcin
|
||||
| Opcode::IaddCarry
|
||||
| Opcode::IaddIfcarry
|
||||
| Opcode::IsubBin
|
||||
| Opcode::IsubIfbin
|
||||
| Opcode::IsubBout
|
||||
| Opcode::IsubIfbout
|
||||
| Opcode::IsubBorrow
|
||||
| Opcode::IsubIfborrow
|
||||
| Opcode::UaddOverflowTrap
|
||||
| Opcode::BandImm
|
||||
| Opcode::BorImm
|
||||
| Opcode::BxorImm
|
||||
@@ -533,8 +520,7 @@ fn lower_insn_to_regs(
|
||||
| Opcode::IshlImm
|
||||
| Opcode::UshrImm
|
||||
| Opcode::SshrImm
|
||||
| Opcode::IcmpImm
|
||||
| Opcode::IfcmpImm => {
|
||||
| Opcode::IcmpImm => {
|
||||
panic!("ALU+imm and ALU+carry ops should not appear here!");
|
||||
}
|
||||
|
||||
|
||||
@@ -172,8 +172,7 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
|
||||
| ir::Opcode::IrsubImm
|
||||
| ir::Opcode::ImulImm
|
||||
| ir::Opcode::SdivImm
|
||||
| ir::Opcode::SremImm
|
||||
| ir::Opcode::IfcmpImm => true,
|
||||
| ir::Opcode::SremImm => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@@ -229,10 +228,6 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
|
||||
ir::Opcode::UremImm => {
|
||||
replace.urem(arg, imm);
|
||||
}
|
||||
// comparisons
|
||||
ir::Opcode::IfcmpImm => {
|
||||
replace.ifcmp(arg, imm);
|
||||
}
|
||||
_ => prev_pos = pos.position(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,7 +142,6 @@ fn trivially_unsafe_for_licm(opcode: Opcode) -> bool {
|
||||
|| opcode.is_return()
|
||||
|| opcode.can_trap()
|
||||
|| opcode.other_side_effects()
|
||||
|| opcode.writes_cpu_flags()
|
||||
}
|
||||
|
||||
fn is_unsafe_load(inst_data: &InstructionData) -> bool {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ir::{types, Inst, Value, ValueList};
|
||||
use crate::ir::{Inst, Value, ValueList};
|
||||
use crate::machinst::{get_output_reg, InsnOutput};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
@@ -775,16 +775,8 @@ where
|
||||
for i in 0..outputs.len() {
|
||||
let regs = temp_regs[i];
|
||||
let dsts = get_output_reg(isle_ctx.lower_ctx, outputs[i]);
|
||||
let ty = isle_ctx
|
||||
.lower_ctx
|
||||
.output_ty(outputs[i].insn, outputs[i].output);
|
||||
if ty == types::IFLAGS || ty == types::FFLAGS {
|
||||
// Flags values do not occupy any registers.
|
||||
assert!(regs.len() == 0);
|
||||
} else {
|
||||
for (dst, temp) in dsts.regs().iter().zip(regs.regs().iter()) {
|
||||
isle_ctx.lower_ctx.set_vreg_alias(dst.to_reg(), *temp);
|
||||
}
|
||||
for (dst, temp) in dsts.regs().iter().zip(regs.regs().iter()) {
|
||||
isle_ctx.lower_ctx.set_vreg_alias(dst.to_reg(), *temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::entity::SecondaryMap;
|
||||
use crate::fx::{FxHashMap, FxHashSet};
|
||||
use crate::inst_predicates::{has_lowering_side_effect, is_constant_64bit};
|
||||
use crate::ir::{
|
||||
types::{FFLAGS, IFLAGS},
|
||||
ArgumentPurpose, Block, Constant, ConstantData, DataFlowGraph, ExternalName, Function,
|
||||
GlobalValue, GlobalValueData, Immediate, Inst, InstructionData, MemFlags, Opcode, RelSourceLoc,
|
||||
Type, Value, ValueDef, ValueLabelAssignments, ValueLabelStart,
|
||||
@@ -1246,12 +1245,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
let val = self.f.dfg.resolve_aliases(val);
|
||||
trace!("put_value_in_regs: val {}", val);
|
||||
|
||||
// Assert that the value is not `iflags`/`fflags`-typed; these
|
||||
// cannot be reified into normal registers. TODO(#3249)
|
||||
// eventually remove the `iflags` type altogether!
|
||||
let ty = self.f.dfg.value_type(val);
|
||||
assert!(ty != IFLAGS && ty != FFLAGS);
|
||||
|
||||
if let Some(inst) = self.f.dfg.value_def(val).inst() {
|
||||
assert!(!self.inst_sunk.contains(&inst));
|
||||
}
|
||||
@@ -1268,6 +1261,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
.inst()
|
||||
.and_then(|inst| self.get_constant(inst))
|
||||
{
|
||||
let ty = self.f.dfg.value_type(val);
|
||||
let regs = self.alloc_tmp(ty);
|
||||
trace!(" -> regs {:?}", regs);
|
||||
assert!(regs.is_valid());
|
||||
|
||||
@@ -18,7 +18,6 @@ fn trivially_unsafe_for_gvn(opcode: Opcode) -> bool {
|
||||
|| opcode.can_trap()
|
||||
|| opcode.other_side_effects()
|
||||
|| opcode.can_store()
|
||||
|| opcode.writes_cpu_flags()
|
||||
}
|
||||
|
||||
/// Test that, if the specified instruction is a load, it doesn't have the `readonly` memflag.
|
||||
|
||||
@@ -699,7 +699,6 @@ mod simplify {
|
||||
imm = imm.wrapping_neg();
|
||||
Opcode::IaddImm
|
||||
}
|
||||
Opcode::Ifcmp => Opcode::IfcmpImm,
|
||||
_ => return,
|
||||
};
|
||||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||
|
||||
@@ -49,7 +49,6 @@ define_passes! {
|
||||
wasm_translate_function: "Translate WASM function",
|
||||
|
||||
verifier: "Verify Cranelift IR",
|
||||
verify_flags: "Verify CPU flags",
|
||||
|
||||
compile: "Compilation passes",
|
||||
try_incremental_cache: "Try loading from incremental cache",
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
//! Verify CPU flags values.
|
||||
|
||||
use crate::entity::{EntitySet, SecondaryMap};
|
||||
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
|
||||
use crate::ir;
|
||||
use crate::ir::instructions::BranchInfo;
|
||||
use crate::packed_option::PackedOption;
|
||||
use crate::timing;
|
||||
use crate::verifier::{VerifierErrors, VerifierStepResult};
|
||||
|
||||
/// Verify that CPU flags are used correctly.
|
||||
///
|
||||
/// The value types `iflags` and `fflags` represent CPU flags which usually live in a
|
||||
/// special-purpose register, so they can't be used as freely as other value types that can live in
|
||||
/// any register.
|
||||
///
|
||||
/// We verify the following conditions:
|
||||
///
|
||||
/// - At most one flags value can be live at a time.
|
||||
/// - A flags value can not be live across an instruction that clobbers the flags.
|
||||
///
|
||||
///
|
||||
pub fn verify_flags(
|
||||
func: &ir::Function,
|
||||
cfg: &ControlFlowGraph,
|
||||
errors: &mut VerifierErrors,
|
||||
) -> VerifierStepResult<()> {
|
||||
let _tt = timing::verify_flags();
|
||||
let mut verifier = FlagsVerifier {
|
||||
func,
|
||||
cfg,
|
||||
livein: SecondaryMap::new(),
|
||||
};
|
||||
verifier.check(errors)
|
||||
}
|
||||
|
||||
struct FlagsVerifier<'a> {
|
||||
func: &'a ir::Function,
|
||||
cfg: &'a ControlFlowGraph,
|
||||
|
||||
/// The single live-in flags value (if any) for each block.
|
||||
livein: SecondaryMap<ir::Block, PackedOption<ir::Value>>,
|
||||
}
|
||||
|
||||
impl<'a> FlagsVerifier<'a> {
|
||||
fn check(&mut self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
||||
// List of blocks that need to be processed. blocks may be re-added to this list when we detect
|
||||
// that one of their successor blocks needs a live-in flags value.
|
||||
let mut worklist = EntitySet::with_capacity(self.func.layout.block_capacity());
|
||||
for block in self.func.layout.blocks() {
|
||||
worklist.insert(block);
|
||||
}
|
||||
|
||||
while let Some(block) = worklist.pop() {
|
||||
if let Some(value) = self.visit_block(block, errors)? {
|
||||
// The block has live-in flags. Check if the value changed.
|
||||
match self.livein[block].expand() {
|
||||
// Revisit any predecessor blocks the first time we see a live-in for `block`.
|
||||
None => {
|
||||
self.livein[block] = value.into();
|
||||
for BlockPredecessor { block: pred, .. } in self.cfg.pred_iter(block) {
|
||||
worklist.insert(pred);
|
||||
}
|
||||
}
|
||||
Some(old) if old != value => {
|
||||
return errors.fatal((
|
||||
block,
|
||||
format!("conflicting live-in CPU flags: {} and {}", old, value),
|
||||
));
|
||||
}
|
||||
x => assert_eq!(x, Some(value)),
|
||||
}
|
||||
} else {
|
||||
// Existing live-in flags should never be able to disappear.
|
||||
assert_eq!(self.livein[block].expand(), None);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check flags usage in `block` and return the live-in flags value, if any.
|
||||
fn visit_block(
|
||||
&self,
|
||||
block: ir::Block,
|
||||
errors: &mut VerifierErrors,
|
||||
) -> VerifierStepResult<Option<ir::Value>> {
|
||||
// The single currently live flags value.
|
||||
let mut live_val = None;
|
||||
|
||||
// Visit instructions backwards so we can track liveness accurately.
|
||||
for inst in self.func.layout.block_insts(block).rev() {
|
||||
// Check if `inst` interferes with existing live flags.
|
||||
if let Some(live) = live_val {
|
||||
for &res in self.func.dfg.inst_results(inst) {
|
||||
if res == live {
|
||||
// We've reached the def of `live_flags`, so it is no longer live above.
|
||||
live_val = None;
|
||||
} else if self.func.dfg.value_type(res).is_flags() {
|
||||
errors
|
||||
.report((inst, format!("{} clobbers live CPU flags in {}", res, live)));
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now look for live ranges of CPU flags that end here.
|
||||
for &arg in self.func.dfg.inst_args(inst) {
|
||||
if self.func.dfg.value_type(arg).is_flags() {
|
||||
merge(&mut live_val, arg, inst, errors)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Include live-in flags to successor blocks.
|
||||
match self.func.dfg.analyze_branch(inst) {
|
||||
BranchInfo::NotABranch => {}
|
||||
BranchInfo::SingleDest(dest, _) => {
|
||||
if let Some(val) = self.livein[dest].expand() {
|
||||
merge(&mut live_val, val, inst, errors)?;
|
||||
}
|
||||
}
|
||||
BranchInfo::Table(jt, dest) => {
|
||||
if let Some(dest) = dest {
|
||||
if let Some(val) = self.livein[dest].expand() {
|
||||
merge(&mut live_val, val, inst, errors)?;
|
||||
}
|
||||
}
|
||||
for dest in self.func.jump_tables[jt].iter() {
|
||||
if let Some(val) = self.livein[*dest].expand() {
|
||||
merge(&mut live_val, val, inst, errors)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the required live-in flags value.
|
||||
Ok(live_val)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge live flags values, or return an error on conflicting values.
|
||||
fn merge(
|
||||
a: &mut Option<ir::Value>,
|
||||
b: ir::Value,
|
||||
inst: ir::Inst,
|
||||
errors: &mut VerifierErrors,
|
||||
) -> VerifierStepResult<()> {
|
||||
if let Some(va) = *a {
|
||||
if b != va {
|
||||
return errors.fatal((
|
||||
inst,
|
||||
format!("conflicting live CPU flags: {} and {}", va, b),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
*a = Some(b);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -57,7 +57,6 @@
|
||||
//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number
|
||||
//! of arguments must match the destination type, and the lane indexes must be in range.
|
||||
|
||||
use self::flags::verify_flags;
|
||||
use crate::dbg::DisplayList;
|
||||
use crate::dominator_tree::DominatorTree;
|
||||
use crate::entity::SparseSet;
|
||||
@@ -81,8 +80,6 @@ use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
mod flags;
|
||||
|
||||
/// A verifier error.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct VerifierError {
|
||||
@@ -1799,8 +1796,6 @@ impl<'a> Verifier<'a> {
|
||||
self.encodable_as_bb(block, errors)?;
|
||||
}
|
||||
|
||||
verify_flags(self.func, &self.expected_cfg, errors)?;
|
||||
|
||||
if !errors.is_empty() {
|
||||
log::warn!(
|
||||
"Found verifier errors in function:\n{}",
|
||||
|
||||
Reference in New Issue
Block a user