x64: Begin migrating branch instructions to ISLE (#4587)
https://github.com/bytecodealliance/wasmtime/pull/4587
This commit is contained in:
@@ -515,6 +515,16 @@
|
|||||||
|
|
||||||
(type BoxVecMachLabel extern (enum))
|
(type BoxVecMachLabel extern (enum))
|
||||||
|
|
||||||
|
(type MachLabelSlice extern (enum))
|
||||||
|
|
||||||
|
;; Extract a the target from a MachLabelSlice with exactly one target.
|
||||||
|
(decl single_target (MachLabel) MachLabelSlice)
|
||||||
|
(extern extractor single_target single_target)
|
||||||
|
|
||||||
|
;; Extract a the targets from a MachLabelSlice with exactly two targets.
|
||||||
|
(decl two_targets (MachLabel MachLabel) MachLabelSlice)
|
||||||
|
(extern extractor two_targets two_targets)
|
||||||
|
|
||||||
;; Get the `OperandSize` for a given `Type`, rounding smaller types up to 32 bits.
|
;; Get the `OperandSize` for a given `Type`, rounding smaller types up to 32 bits.
|
||||||
(decl operand_size_of_type_32_64 (Type) OperandSize)
|
(decl operand_size_of_type_32_64 (Type) OperandSize)
|
||||||
(extern constructor operand_size_of_type_32_64 operand_size_of_type_32_64)
|
(extern constructor operand_size_of_type_32_64 operand_size_of_type_32_64)
|
||||||
@@ -1057,9 +1067,13 @@
|
|||||||
NLE
|
NLE
|
||||||
P
|
P
|
||||||
NP))
|
NP))
|
||||||
|
|
||||||
(decl intcc_to_cc (IntCC) CC)
|
(decl intcc_to_cc (IntCC) CC)
|
||||||
(extern constructor intcc_to_cc intcc_to_cc)
|
(extern constructor intcc_to_cc intcc_to_cc)
|
||||||
|
|
||||||
|
(decl cc_invert (CC) CC)
|
||||||
|
(extern constructor cc_invert cc_invert)
|
||||||
|
|
||||||
(type AvxOpcode extern
|
(type AvxOpcode extern
|
||||||
(enum Vfmadd213ps
|
(enum Vfmadd213ps
|
||||||
Vfmadd213pd))
|
Vfmadd213pd))
|
||||||
@@ -3039,6 +3053,23 @@
|
|||||||
(rule (trap_if_fcmp (FcmpCondResult.OrCondition producer cc1 cc2) tc)
|
(rule (trap_if_fcmp (FcmpCondResult.OrCondition producer cc1 cc2) tc)
|
||||||
(with_flags_side_effect producer (trap_if_or cc1 cc2 tc)))
|
(with_flags_side_effect producer (trap_if_or cc1 cc2 tc)))
|
||||||
|
|
||||||
|
;;;; Jumps ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;; Unconditional jump.
|
||||||
|
(decl jmp_known (MachLabel) SideEffectNoResult)
|
||||||
|
(rule (jmp_known target)
|
||||||
|
(SideEffectNoResult.Inst (MInst.JmpKnown target)))
|
||||||
|
|
||||||
|
;; Conditional jump based on the condition code.
|
||||||
|
(decl jmp_cond (CC MachLabel MachLabel) ConsumesFlags)
|
||||||
|
(rule (jmp_cond cc taken not_taken)
|
||||||
|
(ConsumesFlags.ConsumesFlagsSideEffect (MInst.JmpCond cc taken not_taken)))
|
||||||
|
|
||||||
|
;; Conditional jump based on the result of an icmp.
|
||||||
|
(decl jmp_cond_icmp (IcmpCondResult MachLabel MachLabel) SideEffectNoResult)
|
||||||
|
(rule (jmp_cond_icmp (IcmpCondResult.Condition producer cc) taken not_taken)
|
||||||
|
(with_flags_side_effect producer (jmp_cond cc taken not_taken)))
|
||||||
|
|
||||||
;;;; Comparisons ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;; Comparisons ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(type IcmpCondResult (enum (Condition (producer ProducesFlags) (cc CC))))
|
(type IcmpCondResult (enum (Condition (producer ProducesFlags) (cc CC))))
|
||||||
@@ -3046,6 +3077,10 @@
|
|||||||
(decl icmp_cond_result (ProducesFlags CC) IcmpCondResult)
|
(decl icmp_cond_result (ProducesFlags CC) IcmpCondResult)
|
||||||
(rule (icmp_cond_result producer cc) (IcmpCondResult.Condition producer cc))
|
(rule (icmp_cond_result producer cc) (IcmpCondResult.Condition producer cc))
|
||||||
|
|
||||||
|
(decl invert_icmp_cond_result (IcmpCondResult) IcmpCondResult)
|
||||||
|
(rule (invert_icmp_cond_result (IcmpCondResult.Condition producer cc))
|
||||||
|
(icmp_cond_result producer (cc_invert cc)))
|
||||||
|
|
||||||
;; Lower an Icmp result into a boolean value in a register.
|
;; Lower an Icmp result into a boolean value in a register.
|
||||||
(decl lower_icmp_bool (IcmpCondResult) ValueRegs)
|
(decl lower_icmp_bool (IcmpCondResult) ValueRegs)
|
||||||
(rule (lower_icmp_bool (IcmpCondResult.Condition producer cc))
|
(rule (lower_icmp_bool (IcmpCondResult.Condition producer cc))
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
;; register(s) within which the lowered instruction's result values live.
|
;; register(s) within which the lowered instruction's result values live.
|
||||||
(decl lower (Inst) InstOutput)
|
(decl lower (Inst) InstOutput)
|
||||||
|
|
||||||
|
;; A variant of the main lowering constructor term, used for branches.
|
||||||
|
;; The only difference is that it gets an extra argument holding a vector
|
||||||
|
;; of branch targets to be used.
|
||||||
|
(decl lower_branch (Inst MachLabelSlice) InstOutput)
|
||||||
|
|
||||||
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
;; `i64` and smaller.
|
;; `i64` and smaller.
|
||||||
@@ -2856,3 +2861,30 @@
|
|||||||
(x64_load $I64
|
(x64_load $I64
|
||||||
(Amode.ImmReg 8 (preg_rbp) (mem_flags_trusted))
|
(Amode.ImmReg 8 (preg_rbp) (mem_flags_trusted))
|
||||||
(ExtKind.None)))
|
(ExtKind.None)))
|
||||||
|
|
||||||
|
;; Rules for `jump` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower_branch (jump _ _) (single_target target))
|
||||||
|
(side_effect (jmp_known target)))
|
||||||
|
|
||||||
|
;; Rules for `brif` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower_branch (brif cc (ifcmp a b) _ _) (two_targets taken not_taken))
|
||||||
|
(side_effect (jmp_cond_icmp (emit_cmp cc a b) taken not_taken)))
|
||||||
|
|
||||||
|
;; Rules for `brz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower_branch (brz (icmp cc a b) _ _) (two_targets taken not_taken))
|
||||||
|
(let ((cmp IcmpCondResult (invert_icmp_cond_result (emit_cmp cc a b))))
|
||||||
|
(side_effect (jmp_cond_icmp cmp taken not_taken))))
|
||||||
|
|
||||||
|
;; Rules for `brnz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower_branch (brnz (icmp cc a b) _ _) (two_targets taken not_taken))
|
||||||
|
(side_effect (jmp_cond_icmp (emit_cmp cc a b) taken not_taken)))
|
||||||
|
|
||||||
|
|
||||||
|
;; Rules for `bricmp` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(rule (lower_branch (br_icmp cc a b _ _) (two_targets taken not_taken))
|
||||||
|
(side_effect (jmp_cond_icmp (emit_cmp cc a b) taken not_taken)))
|
||||||
|
|||||||
@@ -2833,6 +2833,24 @@ impl LowerBackend for X64Backend {
|
|||||||
// verifier pass.
|
// verifier pass.
|
||||||
assert!(branches.len() <= 2);
|
assert!(branches.len() <= 2);
|
||||||
|
|
||||||
|
if let Ok(()) = isle::lower_branch(
|
||||||
|
ctx,
|
||||||
|
&self.triple,
|
||||||
|
&self.flags,
|
||||||
|
&self.x64_flags,
|
||||||
|
branches[0],
|
||||||
|
targets,
|
||||||
|
) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let implemented_in_isle = |ctx: &mut C| {
|
||||||
|
unreachable!(
|
||||||
|
"branch implemented in ISLE: inst = `{}`",
|
||||||
|
ctx.dfg().display_inst(branches[0])
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
if branches.len() == 2 {
|
if branches.len() == 2 {
|
||||||
// Must be a conditional branch followed by an unconditional branch.
|
// Must be a conditional branch followed by an unconditional branch.
|
||||||
let op0 = ctx.data(branches[0]).opcode();
|
let op0 = ctx.data(branches[0]).opcode();
|
||||||
@@ -2858,18 +2876,8 @@ impl LowerBackend for X64Backend {
|
|||||||
|
|
||||||
let src_ty = ctx.input_ty(branches[0], 0);
|
let src_ty = ctx.input_ty(branches[0], 0);
|
||||||
|
|
||||||
if let Some(icmp) = matches_input(ctx, flag_input, Opcode::Icmp) {
|
if let Some(_icmp) = matches_input(ctx, flag_input, Opcode::Icmp) {
|
||||||
let cond_code = ctx.data(icmp).cond_code().unwrap();
|
implemented_in_isle(ctx)
|
||||||
let cond_code = emit_cmp(ctx, icmp, cond_code);
|
|
||||||
|
|
||||||
let cond_code = if op0 == Opcode::Brz {
|
|
||||||
cond_code.inverse()
|
|
||||||
} else {
|
|
||||||
cond_code
|
|
||||||
};
|
|
||||||
|
|
||||||
let cc = CC::from_intcc(cond_code);
|
|
||||||
ctx.emit(Inst::jmp_cond(cc, taken, not_taken));
|
|
||||||
} else if let Some(fcmp) = matches_input(ctx, flag_input, Opcode::Fcmp) {
|
} else if let Some(fcmp) = matches_input(ctx, flag_input, Opcode::Fcmp) {
|
||||||
let cond_code = ctx.data(fcmp).fp_cond_code().unwrap();
|
let cond_code = ctx.data(fcmp).fp_cond_code().unwrap();
|
||||||
let cond_code = if op0 == Opcode::Brz {
|
let cond_code = if op0 == Opcode::Brz {
|
||||||
@@ -2960,49 +2968,7 @@ impl LowerBackend for X64Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::BrIcmp => {
|
Opcode::BrIcmp | Opcode::Brif => implemented_in_isle(ctx),
|
||||||
let src_ty = ctx.input_ty(branches[0], 0);
|
|
||||||
if is_int_or_ref_ty(src_ty) || is_bool_ty(src_ty) {
|
|
||||||
let lhs = put_input_in_reg(
|
|
||||||
ctx,
|
|
||||||
InsnInput {
|
|
||||||
insn: branches[0],
|
|
||||||
input: 0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let rhs = input_to_reg_mem_imm(
|
|
||||||
ctx,
|
|
||||||
InsnInput {
|
|
||||||
insn: branches[0],
|
|
||||||
input: 1,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let cc = CC::from_intcc(ctx.data(branches[0]).cond_code().unwrap());
|
|
||||||
// Cranelift's icmp semantics want to compare lhs - rhs, while Intel gives
|
|
||||||
// us dst - src at the machine instruction level, so invert operands.
|
|
||||||
ctx.emit(Inst::cmp_rmi_r(OperandSize::from_ty(src_ty), rhs, lhs));
|
|
||||||
ctx.emit(Inst::jmp_cond(cc, taken, not_taken));
|
|
||||||
} else {
|
|
||||||
unimplemented!("bricmp with non-int type {:?}", src_ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Opcode::Brif => {
|
|
||||||
let flag_input = InsnInput {
|
|
||||||
insn: branches[0],
|
|
||||||
input: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(ifcmp) = matches_input(ctx, flag_input, Opcode::Ifcmp) {
|
|
||||||
let cond_code = ctx.data(branches[0]).cond_code().unwrap();
|
|
||||||
let cond_code = emit_cmp(ctx, ifcmp, cond_code);
|
|
||||||
let cc = CC::from_intcc(cond_code);
|
|
||||||
ctx.emit(Inst::jmp_cond(cc, taken, not_taken));
|
|
||||||
} else {
|
|
||||||
// Should be disallowed by flags checks in verifier.
|
|
||||||
unimplemented!("Brif with non-ifcmp input");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Opcode::Brff => {
|
Opcode::Brff => {
|
||||||
let flag_input = InsnInput {
|
let flag_input = InsnInput {
|
||||||
insn: branches[0],
|
insn: branches[0],
|
||||||
@@ -3039,9 +3005,7 @@ impl LowerBackend for X64Backend {
|
|||||||
// Must be an unconditional branch or trap.
|
// Must be an unconditional branch or trap.
|
||||||
let op = ctx.data(branches[0]).opcode();
|
let op = ctx.data(branches[0]).opcode();
|
||||||
match op {
|
match op {
|
||||||
Opcode::Jump => {
|
Opcode::Jump => implemented_in_isle(ctx),
|
||||||
ctx.emit(Inst::jmp_known(targets[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
Opcode::BrTable => {
|
Opcode::BrTable => {
|
||||||
let jt_size = targets.len() - 1;
|
let jt_size = targets.len() - 1;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ use target_lexicon::Triple;
|
|||||||
|
|
||||||
type BoxCallInfo = Box<CallInfo>;
|
type BoxCallInfo = Box<CallInfo>;
|
||||||
type BoxVecMachLabel = Box<SmallVec<[MachLabel; 4]>>;
|
type BoxVecMachLabel = Box<SmallVec<[MachLabel; 4]>>;
|
||||||
|
type MachLabelSlice = [MachLabel];
|
||||||
|
|
||||||
pub struct SinkableLoad {
|
pub struct SinkableLoad {
|
||||||
inst: Inst,
|
inst: Inst,
|
||||||
@@ -71,6 +72,28 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn lower_branch<C>(
|
||||||
|
lower_ctx: &mut C,
|
||||||
|
triple: &Triple,
|
||||||
|
flags: &Flags,
|
||||||
|
isa_flags: &IsaFlags,
|
||||||
|
branch: Inst,
|
||||||
|
targets: &[MachLabel],
|
||||||
|
) -> Result<(), ()>
|
||||||
|
where
|
||||||
|
C: LowerCtx<I = MInst>,
|
||||||
|
{
|
||||||
|
lower_common(
|
||||||
|
lower_ctx,
|
||||||
|
triple,
|
||||||
|
flags,
|
||||||
|
isa_flags,
|
||||||
|
&[],
|
||||||
|
branch,
|
||||||
|
|cx, insn| generated_code::constructor_lower_branch(cx, insn, targets),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl<C> Context for IsleContext<'_, C, Flags, IsaFlags, 6>
|
impl<C> Context for IsleContext<'_, C, Flags, IsaFlags, 6>
|
||||||
where
|
where
|
||||||
C: LowerCtx<I = MInst>,
|
C: LowerCtx<I = MInst>,
|
||||||
@@ -562,6 +585,11 @@ where
|
|||||||
CC::from_intcc(*intcc)
|
CC::from_intcc(*intcc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cc_invert(&mut self, cc: &CC) -> CC {
|
||||||
|
cc.invert()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sum_extend_fits_in_32_bits(
|
fn sum_extend_fits_in_32_bits(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -675,6 +703,24 @@ where
|
|||||||
|
|
||||||
output_reg.to_reg()
|
output_reg.to_reg()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn single_target(&mut self, targets: &MachLabelSlice) -> Option<MachLabel> {
|
||||||
|
if targets.len() == 1 {
|
||||||
|
Some(targets[0])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn two_targets(&mut self, targets: &MachLabelSlice) -> Option<(MachLabel, MachLabel)> {
|
||||||
|
if targets.len() == 2 {
|
||||||
|
Some((targets[0], targets[1]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> IsleContext<'_, C, Flags, IsaFlags, 6>
|
impl<C> IsleContext<'_, C, Flags, IsaFlags, 6>
|
||||||
|
|||||||
Reference in New Issue
Block a user