Rework x64 addressing-mode lowering to be slightly more flexible. (#4080)

This PR refactors the x64 backend address-mode lowering to use an
incremental-build approach, where it considers each node in a tree of
`iadd`s that feed into a load/store address and, at each step, builds
the best possible `Amode`. It will combine an arbitrary number of
constant offsets (an extension beyond the current rules), and can
capture a left-shifted (scaled) index in any position of the tree
(another extension).

This doesn't have any measurable performance improvement on our Wasm
benchmarks in Sightglass, unfortunately, because the IR lowered from
wasm32 will do address computation in 32 bits and then `uextend` it to
add to the 64-bit heap base. We can't quite lift the 32-bit adds to 64
bits because this loses the wraparound semantics.

(We could label adds as "expected not to overflow", and allow *those* to
be lifted to 64 bit operations; wasm32 heap address computation should
fit this.  This is `add nuw` (no unsigned wrap) in LLVM IR terms. That's
likely my next step.)

Nevertheless, (i) this generalizes the cases we can handle, which should
be a good thing, all other things being equal (and in this case, no
compile time impact was measured); and (ii) might benefit non-Wasm
frontends.
This commit is contained in:
Chris Fallin
2022-05-02 16:20:39 -07:00
committed by GitHub
parent 61dc38c065
commit f85047b084
13 changed files with 1416 additions and 742 deletions

View File

@@ -21,6 +21,12 @@ pub trait Context {
fn unpack_value_array_3(&mut self, arg0: &ValueArray3) -> (Value, Value, Value);
fn pack_value_array_3(&mut self, arg0: Value, arg1: Value, arg2: Value) -> ValueArray3;
fn u32_add(&mut self, arg0: u32, arg1: u32) -> u32;
fn s32_add_fallible(&mut self, arg0: u32, arg1: u32) -> Option<u32>;
fn u32_nonnegative(&mut self, arg0: u32) -> Option<u32>;
fn offset32(&mut self, arg0: Offset32) -> Option<u32>;
fn u32_lteq(&mut self, arg0: u32, arg1: u32) -> Option<Unit>;
fn simm32(&mut self, arg0: Imm64) -> Option<u32>;
fn uimm8(&mut self, arg0: Imm64) -> Option<u8>;
fn u8_and(&mut self, arg0: u8, arg1: u8) -> u8;
fn value_reg(&mut self, arg0: Reg) -> ValueRegs;
fn value_regs(&mut self, arg0: Reg, arg1: Reg) -> ValueRegs;
@@ -32,19 +38,22 @@ pub trait Context {
fn output_builder_push(&mut self, arg0: &InstOutputBuilder, arg1: ValueRegs) -> Unit;
fn output_builder_finish(&mut self, arg0: &InstOutputBuilder) -> InstOutput;
fn temp_writable_reg(&mut self, arg0: Type) -> WritableReg;
fn invalid_reg_etor(&mut self, arg0: Reg) -> Option<()>;
fn invalid_reg(&mut self) -> Reg;
fn valid_reg(&mut self, arg0: Reg) -> Option<()>;
fn put_in_reg(&mut self, arg0: Value) -> Reg;
fn put_in_regs(&mut self, arg0: Value) -> ValueRegs;
fn ensure_in_vreg(&mut self, arg0: Reg, arg1: Type) -> Reg;
fn value_regs_get(&mut self, arg0: ValueRegs, arg1: usize) -> Reg;
fn u8_as_u64(&mut self, arg0: u8) -> u64;
fn u16_as_u64(&mut self, arg0: u16) -> u64;
fn u32_as_u64(&mut self, arg0: u32) -> u64;
fn i64_as_u64(&mut self, arg0: i64) -> u64;
fn u64_add(&mut self, arg0: u64, arg1: u64) -> u64;
fn u64_sub(&mut self, arg0: u64, arg1: u64) -> u64;
fn u64_and(&mut self, arg0: u64, arg1: u64) -> u64;
fn ty_bits(&mut self, arg0: Type) -> u8;
fn u8_as_u32(&mut self, arg0: u8) -> Option<u32>;
fn u8_as_u64(&mut self, arg0: u8) -> Option<u64>;
fn u16_as_u64(&mut self, arg0: u16) -> Option<u64>;
fn u32_as_u64(&mut self, arg0: u32) -> Option<u64>;
fn i64_as_u64(&mut self, arg0: i64) -> Option<u64>;
fn u64_add(&mut self, arg0: u64, arg1: u64) -> Option<u64>;
fn u64_sub(&mut self, arg0: u64, arg1: u64) -> Option<u64>;
fn u64_and(&mut self, arg0: u64, arg1: u64) -> Option<u64>;
fn ty_bits(&mut self, arg0: Type) -> Option<u8>;
fn ty_bits_u16(&mut self, arg0: Type) -> u16;
fn ty_bits_u64(&mut self, arg0: Type) -> u64;
fn ty_mask(&mut self, arg0: Type) -> u64;
@@ -137,14 +146,14 @@ pub trait Context {
fn rotr_opposite_amount(&mut self, arg0: Type, arg1: ImmShift) -> ImmShift;
}
/// Internal type SideEffectNoResult: defined at src/prelude.isle line 412.
/// Internal type SideEffectNoResult: defined at src/prelude.isle line 447.
#[derive(Clone, Debug)]
pub enum SideEffectNoResult {
Inst { inst: MInst },
Inst2 { inst1: MInst, inst2: MInst },
}
/// Internal type ProducesFlags: defined at src/prelude.isle line 439.
/// Internal type ProducesFlags: defined at src/prelude.isle line 474.
#[derive(Clone, Debug)]
pub enum ProducesFlags {
ProducesFlagsSideEffect { inst: MInst },
@@ -152,7 +161,7 @@ pub enum ProducesFlags {
ProducesFlagsReturnsResultWithConsumer { inst: MInst, result: Reg },
}
/// Internal type ConsumesFlags: defined at src/prelude.isle line 450.
/// Internal type ConsumesFlags: defined at src/prelude.isle line 485.
#[derive(Clone, Debug)]
pub enum ConsumesFlags {
ConsumesFlagsReturnsResultWithProducer {
@@ -1049,7 +1058,7 @@ pub enum AtomicRMWLoopOp {
// Generated as internal constructor for term output_reg.
pub fn constructor_output_reg<C: Context>(ctx: &mut C, arg0: Reg) -> Option<InstOutput> {
let pattern0_0 = arg0;
// Rule at src/prelude.isle line 86.
// Rule at src/prelude.isle line 113.
let expr0_0 = C::value_reg(ctx, pattern0_0);
let expr1_0 = C::output(ctx, expr0_0);
return Some(expr1_0);
@@ -1058,7 +1067,7 @@ pub fn constructor_output_reg<C: Context>(ctx: &mut C, arg0: Reg) -> Option<Inst
// Generated as internal constructor for term output_value.
pub fn constructor_output_value<C: Context>(ctx: &mut C, arg0: Value) -> Option<InstOutput> {
let pattern0_0 = arg0;
// Rule at src/prelude.isle line 90.
// Rule at src/prelude.isle line 117.
let expr0_0 = C::put_in_regs(ctx, pattern0_0);
let expr1_0 = C::output(ctx, expr0_0);
return Some(expr1_0);
@@ -1067,7 +1076,7 @@ pub fn constructor_output_value<C: Context>(ctx: &mut C, arg0: Value) -> Option<
// Generated as internal constructor for term temp_reg.
pub fn constructor_temp_reg<C: Context>(ctx: &mut C, arg0: Type) -> Option<Reg> {
let pattern0_0 = arg0;
// Rule at src/prelude.isle line 110.
// Rule at src/prelude.isle line 137.
let expr0_0 = C::temp_writable_reg(ctx, pattern0_0);
let expr1_0 = C::writable_reg_to_reg(ctx, expr0_0);
return Some(expr1_0);
@@ -1076,7 +1085,7 @@ pub fn constructor_temp_reg<C: Context>(ctx: &mut C, arg0: Type) -> Option<Reg>
// Generated as internal constructor for term lo_reg.
pub fn constructor_lo_reg<C: Context>(ctx: &mut C, arg0: Value) -> Option<Reg> {
let pattern0_0 = arg0;
// Rule at src/prelude.isle line 150.
// Rule at src/prelude.isle line 182.
let expr0_0 = C::put_in_regs(ctx, pattern0_0);
let expr1_0: usize = 0;
let expr2_0 = C::value_regs_get(ctx, expr0_0, expr1_0);
@@ -1093,7 +1102,7 @@ pub fn constructor_side_effect<C: Context>(
&SideEffectNoResult::Inst {
inst: ref pattern1_0,
} => {
// Rule at src/prelude.isle line 420.
// Rule at src/prelude.isle line 455.
let expr0_0 = C::emit(ctx, pattern1_0);
let expr1_0 = C::output_none(ctx);
return Some(expr1_0);
@@ -1102,7 +1111,7 @@ pub fn constructor_side_effect<C: Context>(
inst1: ref pattern1_0,
inst2: ref pattern1_1,
} => {
// Rule at src/prelude.isle line 423.
// Rule at src/prelude.isle line 458.
let expr0_0 = C::emit(ctx, pattern1_0);
let expr1_0 = C::emit(ctx, pattern1_1);
let expr2_0 = C::output_none(ctx);
@@ -1129,7 +1138,7 @@ pub fn constructor_side_effect_concat<C: Context>(
inst: ref pattern3_0,
} = pattern2_0
{
// Rule at src/prelude.isle line 429.
// Rule at src/prelude.isle line 464.
let expr0_0 = SideEffectNoResult::Inst2 {
inst1: pattern1_0.clone(),
inst2: pattern3_0.clone(),
@@ -1151,7 +1160,7 @@ pub fn constructor_produces_flags_get_reg<C: Context>(
result: pattern1_1,
} = pattern0_0
{
// Rule at src/prelude.isle line 466.
// Rule at src/prelude.isle line 501.
return Some(pattern1_1);
}
return None;
@@ -1168,7 +1177,7 @@ pub fn constructor_produces_flags_ignore<C: Context>(
inst: ref pattern1_0,
result: pattern1_1,
} => {
// Rule at src/prelude.isle line 471.
// Rule at src/prelude.isle line 506.
let expr0_0 = ProducesFlags::ProducesFlagsSideEffect {
inst: pattern1_0.clone(),
};
@@ -1178,7 +1187,7 @@ pub fn constructor_produces_flags_ignore<C: Context>(
inst: ref pattern1_0,
result: pattern1_1,
} => {
// Rule at src/prelude.isle line 473.
// Rule at src/prelude.isle line 508.
let expr0_0 = ProducesFlags::ProducesFlagsSideEffect {
inst: pattern1_0.clone(),
};
@@ -1207,7 +1216,7 @@ pub fn constructor_consumes_flags_concat<C: Context>(
result: pattern3_1,
} = pattern2_0
{
// Rule at src/prelude.isle line 480.
// Rule at src/prelude.isle line 515.
let expr0_0 = C::value_regs(ctx, pattern1_1, pattern3_1);
let expr1_0 = ConsumesFlags::ConsumesFlagsTwiceReturnsValueRegs {
inst1: pattern1_0.clone(),
@@ -1237,7 +1246,7 @@ pub fn constructor_with_flags<C: Context>(
inst: ref pattern3_0,
result: pattern3_1,
} => {
// Rule at src/prelude.isle line 505.
// Rule at src/prelude.isle line 540.
let expr0_0 = C::emit(ctx, pattern1_0);
let expr1_0 = C::emit(ctx, pattern3_0);
let expr2_0 = C::value_reg(ctx, pattern3_1);
@@ -1248,7 +1257,7 @@ pub fn constructor_with_flags<C: Context>(
inst2: ref pattern3_1,
result: pattern3_2,
} => {
// Rule at src/prelude.isle line 511.
// Rule at src/prelude.isle line 546.
let expr0_0 = C::emit(ctx, pattern1_0);
let expr1_0 = C::emit(ctx, pattern3_0);
let expr2_0 = C::emit(ctx, pattern3_1);
@@ -1261,7 +1270,7 @@ pub fn constructor_with_flags<C: Context>(
inst4: ref pattern3_3,
result: pattern3_4,
} => {
// Rule at src/prelude.isle line 523.
// Rule at src/prelude.isle line 558.
let expr0_0 = C::emit(ctx, pattern1_0);
let expr1_0 = C::emit(ctx, pattern3_0);
let expr2_0 = C::emit(ctx, pattern3_1);
@@ -1282,7 +1291,7 @@ pub fn constructor_with_flags<C: Context>(
result: pattern3_1,
} = pattern2_0
{
// Rule at src/prelude.isle line 499.
// Rule at src/prelude.isle line 534.
let expr0_0 = C::emit(ctx, pattern1_0);
let expr1_0 = C::emit(ctx, pattern3_0);
let expr2_0 = C::value_regs(ctx, pattern1_1, pattern3_1);
@@ -1302,7 +1311,7 @@ pub fn constructor_with_flags_reg<C: Context>(
) -> Option<Reg> {
let pattern0_0 = arg0;
let pattern1_0 = arg1;
// Rule at src/prelude.isle line 540.
// Rule at src/prelude.isle line 575.
let expr0_0 = constructor_with_flags(ctx, pattern0_0, pattern1_0)?;
let expr1_0: usize = 0;
let expr2_0 = C::value_regs_get(ctx, expr0_0, expr1_0);
@@ -3154,7 +3163,7 @@ pub fn constructor_put_in_reg_sext32<C: Context>(ctx: &mut C, arg0: Value) -> Op
// Rule at src/isa/aarch64/inst.isle line 1895.
let expr0_0 = C::put_in_reg(ctx, pattern0_0);
let expr1_0: bool = true;
let expr2_0 = C::ty_bits(ctx, pattern2_0);
let expr2_0 = C::ty_bits(ctx, pattern2_0)?;
let expr3_0: u8 = 32;
let expr4_0 = constructor_extend(ctx, expr0_0, expr1_0, expr2_0, expr3_0)?;
return Some(expr4_0);
@@ -3180,7 +3189,7 @@ pub fn constructor_put_in_reg_zext32<C: Context>(ctx: &mut C, arg0: Value) -> Op
// Rule at src/isa/aarch64/inst.isle line 1904.
let expr0_0 = C::put_in_reg(ctx, pattern0_0);
let expr1_0: bool = false;
let expr2_0 = C::ty_bits(ctx, pattern2_0);
let expr2_0 = C::ty_bits(ctx, pattern2_0)?;
let expr3_0: u8 = 32;
let expr4_0 = constructor_extend(ctx, expr0_0, expr1_0, expr2_0, expr3_0)?;
return Some(expr4_0);
@@ -3201,7 +3210,7 @@ pub fn constructor_put_in_reg_sext64<C: Context>(ctx: &mut C, arg0: Value) -> Op
// Rule at src/isa/aarch64/inst.isle line 1913.
let expr0_0 = C::put_in_reg(ctx, pattern0_0);
let expr1_0: bool = true;
let expr2_0 = C::ty_bits(ctx, pattern2_0);
let expr2_0 = C::ty_bits(ctx, pattern2_0)?;
let expr3_0: u8 = 64;
let expr4_0 = constructor_extend(ctx, expr0_0, expr1_0, expr2_0, expr3_0)?;
return Some(expr4_0);
@@ -3222,7 +3231,7 @@ pub fn constructor_put_in_reg_zext64<C: Context>(ctx: &mut C, arg0: Value) -> Op
// Rule at src/isa/aarch64/inst.isle line 1921.
let expr0_0 = C::put_in_reg(ctx, pattern0_0);
let expr1_0: bool = false;
let expr2_0 = C::ty_bits(ctx, pattern2_0);
let expr2_0 = C::ty_bits(ctx, pattern2_0)?;
let expr3_0: u8 = 64;
let expr4_0 = constructor_extend(ctx, expr0_0, expr1_0, expr2_0, expr3_0)?;
return Some(expr4_0);
@@ -5936,7 +5945,7 @@ pub fn constructor_lower<C: Context>(ctx: &mut C, arg0: Inst) -> Option<InstOutp
let expr3_0 = C::zero_reg(ctx);
let expr4_0 = constructor_madd(ctx, expr2_0, expr0_0, expr1_0, expr3_0)?;
let expr5_0: Type = I64;
let expr6_0 = C::ty_bits(ctx, pattern3_0);
let expr6_0 = C::ty_bits(ctx, pattern3_0)?;
let expr7_0 = C::imm_shift_from_u8(ctx, expr6_0);
let expr8_0 = constructor_lsr_imm(ctx, expr5_0, expr4_0, expr7_0)?;
let expr9_0 = C::value_reg(ctx, expr8_0);
@@ -5952,7 +5961,7 @@ pub fn constructor_lower<C: Context>(ctx: &mut C, arg0: Inst) -> Option<InstOutp
let expr3_0 = C::zero_reg(ctx);
let expr4_0 = constructor_madd(ctx, expr2_0, expr0_0, expr1_0, expr3_0)?;
let expr5_0: Type = I64;
let expr6_0 = C::ty_bits(ctx, pattern3_0);
let expr6_0 = C::ty_bits(ctx, pattern3_0)?;
let expr7_0 = C::imm_shift_from_u8(ctx, expr6_0);
let expr8_0 = constructor_asr_imm(ctx, expr5_0, expr4_0, expr7_0)?;
let expr9_0 = constructor_output_reg(ctx, expr8_0)?;
@@ -6628,8 +6637,8 @@ pub fn constructor_lower<C: Context>(ctx: &mut C, arg0: Inst) -> Option<InstOutp
// Rule at src/isa/aarch64/lower.isle line 485.
let expr0_0 = C::put_in_reg(ctx, pattern5_1);
let expr1_0: bool = false;
let expr2_0 = C::ty_bits(ctx, pattern7_0);
let expr3_0 = C::ty_bits(ctx, pattern3_0);
let expr2_0 = C::ty_bits(ctx, pattern7_0)?;
let expr3_0 = C::ty_bits(ctx, pattern3_0)?;
let expr4_0 =
constructor_extend(ctx, expr0_0, expr1_0, expr2_0, expr3_0)?;
let expr5_0 = constructor_output_reg(ctx, expr4_0)?;
@@ -6667,8 +6676,8 @@ pub fn constructor_lower<C: Context>(ctx: &mut C, arg0: Inst) -> Option<InstOutp
// Rule at src/isa/aarch64/lower.isle line 517.
let expr0_0 = C::put_in_reg(ctx, pattern5_1);
let expr1_0: bool = true;
let expr2_0 = C::ty_bits(ctx, pattern7_0);
let expr3_0 = C::ty_bits(ctx, pattern3_0);
let expr2_0 = C::ty_bits(ctx, pattern7_0)?;
let expr3_0 = C::ty_bits(ctx, pattern3_0)?;
let expr4_0 =
constructor_extend(ctx, expr0_0, expr1_0, expr2_0, expr3_0)?;
let expr5_0 = constructor_output_reg(ctx, expr4_0)?;
@@ -7250,7 +7259,7 @@ pub fn constructor_small_rotr<C: Context>(
let expr1_0 = C::rotr_mask(ctx, pattern0_0);
let expr2_0 = constructor_and_imm(ctx, expr0_0, pattern2_0, expr1_0)?;
let expr3_0: Type = I32;
let expr4_0 = C::ty_bits(ctx, pattern0_0);
let expr4_0 = C::ty_bits(ctx, pattern0_0)?;
let expr5_0 = C::u8_into_imm12(ctx, expr4_0);
let expr6_0 = constructor_sub_imm(ctx, expr3_0, expr2_0, expr5_0)?;
let expr7_0: Type = I32;