Merge pull request #4683 from roc-lang/gen-wasm-shr-128

gen_wasm: Implement u128 right shift by delegating to compiler_rt
This commit is contained in:
Brian Carroll 2023-01-24 00:36:27 +00:00 committed by GitHub
commit ac45fa2bba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 46 deletions

View file

@ -993,33 +993,11 @@ wymix = \a, b ->
wymum : U64, U64 -> { lower : U64, upper : U64 }
wymum = \a, b ->
# uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;
# uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;
# lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c;
ha = Num.shiftRightZfBy a 32
hb = Num.shiftRightZfBy b 32
la = Num.bitwiseAnd a 0x0000_0000_FFFF_FFFF
lb = Num.bitwiseAnd b 0x0000_0000_FFFF_FFFF
rh = ha * hb
rm0 = ha * lb
rm1 = hb * la
rl = la * lb
t = Num.addWrap rl (Num.shiftLeftBy rm0 32)
c = if t < rl then 1 else 0
lower = Num.addWrap t (Num.shiftLeftBy rm1 32)
c2 = c + (if lower < t then 1 else 0)
upper =
rh
|> Num.addWrap (Num.shiftRightZfBy rm0 32)
|> Num.addWrap (Num.shiftRightZfBy rm1 32)
|> Num.addWrap c2
r = Num.toU128 a * Num.toU128 b
lower = Num.toU64 r
upper = Num.shiftRightZfBy r 64 |> Num.toU64
# TODO: switch back to this once wasm supports bit shifting a U128.
# The above code is manually doing the 128bit multiplication.
# r = Num.toU128 a * Num.toU128 b
# lower = Num.toU64 r
# upper = Num.shiftRightZfBy r 64 |> Num.toU64
# This is the more robust form.
# This is the more robust form, which we may look into later
# { lower: Num.bitwiseXor a lower, upper: Num.bitwiseXor b upper }
{ lower, upper }

View file

@ -1708,12 +1708,11 @@ impl<'a> LowLevelCall<'a> {
backend.code_builder.i64_extend_u_i32();
backend.code_builder.i64_shr_u();
}
I128 => todo!("{:?} for I128", self.lowlevel),
I128 => self.load_args_and_call_zig(backend, "__lshrti3"), // from compiler_rt
_ => panic_ret_type(),
}
}
NumIntCast => {
self.load_args(backend);
let arg_layout = backend.storage.symbol_layouts[&self.arguments[0]];
let arg_type = CodeGenNumType::from(arg_layout);
let arg_width = match backend.layout_interner.get(arg_layout) {
@ -1729,19 +1728,56 @@ impl<'a> LowLevelCall<'a> {
};
match (ret_type, arg_type) {
(I32, I32) => self.wrap_small_int(backend, ret_width),
(I32, I32) => {
self.load_args(backend);
self.wrap_small_int(backend, ret_width);
}
(I32, I64) => {
self.load_args(backend);
backend.code_builder.i32_wrap_i64();
self.wrap_small_int(backend, ret_width);
}
(I32, I128) => {
self.load_args(backend);
backend.code_builder.i32_load(Align::Bytes4, 0);
}
(I64, I32) => {
self.load_args(backend);
if arg_width.is_signed() {
backend.code_builder.i64_extend_s_i32()
} else {
backend.code_builder.i64_extend_u_i32()
}
}
(I64, I64) => {}
(I64, I64) => {
self.load_args(backend);
}
(I64, I128) => {
let (frame_ptr, offset) = match backend.storage.get(&self.arguments[0]) {
StoredValue::StackMemory { location, .. } => {
location.local_and_offset(backend.storage.stack_frame_pointer)
}
_ => internal_error!("I128 should be in stack memory"),
};
backend.code_builder.get_local(frame_ptr);
backend.code_builder.i64_load(Align::Bytes8, offset);
}
(I128, I64) => {
// Symbols are loaded as if for a call, so the i128 "return address" and i64 value are on the value stack
self.load_args(backend);
backend.code_builder.i64_store(Align::Bytes8, 0);
// Zero the most significant 64 bits
let (frame_ptr, offset) = match &self.ret_storage {
StoredValue::StackMemory { location, .. } => {
location.local_and_offset(backend.storage.stack_frame_pointer)
}
_ => internal_error!("I128 should be in stack memory"),
};
backend.code_builder.get_local(frame_ptr);
backend.code_builder.i64_const(0);
backend.code_builder.i64_store(Align::Bytes8, offset + 8);
}
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
}

View file

@ -2156,6 +2156,16 @@ fn shift_right_zf_by() {
assert_evals_to!("Num.shiftRightZfBy 0b0000_0010u8 1", 0b0000_0001u8, u8);
assert_evals_to!("Num.shiftRightZfBy 0b0000_1100u8 2", 0b0000_0011u8, u8);
assert_evals_to!("Num.shiftRightZfBy 0b1000_0000u8 12", 0b0000_0000u8, u8);
assert_evals_to!(
"Num.shiftRightZfBy 0xffff_0000_0000_0000_0000_0000_0000_ffffu128 4",
0x0fff_f000_0000_0000_0000_0000_0000_0fffu128,
u128
);
assert_evals_to!(
"Num.shiftRightZfBy 0xaaaa_0000_0000_bbbb_ffff_ffff_ffff_ffffu128 68",
0x0000_0000_0000_0000_0aaa_a000_0000_0bbbu128,
u128
);
}
#[test]

View file

@ -1,22 +1,22 @@
procedure Dict.1 (Dict.518):
let Dict.521 : List {[], []} = Array [];
let Dict.528 : U64 = 0i64;
let Dict.529 : U64 = 8i64;
let Dict.522 : List U64 = CallByName List.11 Dict.528 Dict.529;
let Dict.525 : I8 = CallByName Dict.34;
let Dict.526 : U64 = 8i64;
let Dict.523 : List I8 = CallByName List.11 Dict.525 Dict.526;
let Dict.524 : U64 = 0i64;
let Dict.520 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.521, Dict.522, Dict.523, Dict.524};
ret Dict.520;
procedure Dict.1 (Dict.508):
let Dict.511 : List {[], []} = Array [];
let Dict.518 : U64 = 0i64;
let Dict.519 : U64 = 8i64;
let Dict.512 : List U64 = CallByName List.11 Dict.518 Dict.519;
let Dict.515 : I8 = CallByName Dict.34;
let Dict.516 : U64 = 8i64;
let Dict.513 : List I8 = CallByName List.11 Dict.515 Dict.516;
let Dict.514 : U64 = 0i64;
let Dict.510 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.511, Dict.512, Dict.513, Dict.514};
ret Dict.510;
procedure Dict.34 ():
let Dict.527 : I8 = -128i64;
ret Dict.527;
let Dict.517 : I8 = -128i64;
ret Dict.517;
procedure Dict.4 (Dict.507):
let Dict.85 : U64 = StructAtIndex 3 Dict.507;
dec Dict.507;
procedure Dict.4 (Dict.497):
let Dict.85 : U64 = StructAtIndex 3 Dict.497;
dec Dict.497;
ret Dict.85;
procedure List.11 (List.114, List.115):