mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
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:
commit
ac45fa2bba
4 changed files with 70 additions and 46 deletions
|
@ -993,33 +993,11 @@ wymix = \a, b ->
|
||||||
|
|
||||||
wymum : U64, U64 -> { lower : U64, upper : U64 }
|
wymum : U64, U64 -> { lower : U64, upper : U64 }
|
||||||
wymum = \a, b ->
|
wymum = \a, b ->
|
||||||
# uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;
|
r = Num.toU128 a * Num.toU128 b
|
||||||
# uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;
|
lower = Num.toU64 r
|
||||||
# lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c;
|
upper = Num.shiftRightZfBy r 64 |> Num.toU64
|
||||||
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
|
|
||||||
|
|
||||||
# TODO: switch back to this once wasm supports bit shifting a U128.
|
# This is the more robust form, which we may look into later
|
||||||
# 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.
|
|
||||||
# { lower: Num.bitwiseXor a lower, upper: Num.bitwiseXor b upper }
|
# { lower: Num.bitwiseXor a lower, upper: Num.bitwiseXor b upper }
|
||||||
{ lower, upper }
|
{ lower, upper }
|
||||||
|
|
||||||
|
|
|
@ -1708,12 +1708,11 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend.code_builder.i64_extend_u_i32();
|
backend.code_builder.i64_extend_u_i32();
|
||||||
backend.code_builder.i64_shr_u();
|
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(),
|
_ => panic_ret_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumIntCast => {
|
NumIntCast => {
|
||||||
self.load_args(backend);
|
|
||||||
let arg_layout = backend.storage.symbol_layouts[&self.arguments[0]];
|
let arg_layout = backend.storage.symbol_layouts[&self.arguments[0]];
|
||||||
let arg_type = CodeGenNumType::from(arg_layout);
|
let arg_type = CodeGenNumType::from(arg_layout);
|
||||||
let arg_width = match backend.layout_interner.get(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) {
|
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) => {
|
(I32, I64) => {
|
||||||
|
self.load_args(backend);
|
||||||
backend.code_builder.i32_wrap_i64();
|
backend.code_builder.i32_wrap_i64();
|
||||||
self.wrap_small_int(backend, ret_width);
|
self.wrap_small_int(backend, ret_width);
|
||||||
}
|
}
|
||||||
|
(I32, I128) => {
|
||||||
|
self.load_args(backend);
|
||||||
|
backend.code_builder.i32_load(Align::Bytes4, 0);
|
||||||
|
}
|
||||||
(I64, I32) => {
|
(I64, I32) => {
|
||||||
|
self.load_args(backend);
|
||||||
if arg_width.is_signed() {
|
if arg_width.is_signed() {
|
||||||
backend.code_builder.i64_extend_s_i32()
|
backend.code_builder.i64_extend_s_i32()
|
||||||
} else {
|
} else {
|
||||||
backend.code_builder.i64_extend_u_i32()
|
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),
|
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_0010u8 1", 0b0000_0001u8, u8);
|
||||||
assert_evals_to!("Num.shiftRightZfBy 0b0000_1100u8 2", 0b0000_0011u8, 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 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]
|
#[test]
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
procedure Dict.1 (Dict.518):
|
procedure Dict.1 (Dict.508):
|
||||||
let Dict.521 : List {[], []} = Array [];
|
let Dict.511 : List {[], []} = Array [];
|
||||||
let Dict.528 : U64 = 0i64;
|
let Dict.518 : U64 = 0i64;
|
||||||
let Dict.529 : U64 = 8i64;
|
let Dict.519 : U64 = 8i64;
|
||||||
let Dict.522 : List U64 = CallByName List.11 Dict.528 Dict.529;
|
let Dict.512 : List U64 = CallByName List.11 Dict.518 Dict.519;
|
||||||
let Dict.525 : I8 = CallByName Dict.34;
|
let Dict.515 : I8 = CallByName Dict.34;
|
||||||
let Dict.526 : U64 = 8i64;
|
let Dict.516 : U64 = 8i64;
|
||||||
let Dict.523 : List I8 = CallByName List.11 Dict.525 Dict.526;
|
let Dict.513 : List I8 = CallByName List.11 Dict.515 Dict.516;
|
||||||
let Dict.524 : U64 = 0i64;
|
let Dict.514 : U64 = 0i64;
|
||||||
let Dict.520 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.521, Dict.522, Dict.523, Dict.524};
|
let Dict.510 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.511, Dict.512, Dict.513, Dict.514};
|
||||||
ret Dict.520;
|
ret Dict.510;
|
||||||
|
|
||||||
procedure Dict.34 ():
|
procedure Dict.34 ():
|
||||||
let Dict.527 : I8 = -128i64;
|
let Dict.517 : I8 = -128i64;
|
||||||
ret Dict.527;
|
ret Dict.517;
|
||||||
|
|
||||||
procedure Dict.4 (Dict.507):
|
procedure Dict.4 (Dict.497):
|
||||||
let Dict.85 : U64 = StructAtIndex 3 Dict.507;
|
let Dict.85 : U64 = StructAtIndex 3 Dict.497;
|
||||||
dec Dict.507;
|
dec Dict.497;
|
||||||
ret Dict.85;
|
ret Dict.85;
|
||||||
|
|
||||||
procedure List.11 (List.114, List.115):
|
procedure List.11 (List.114, List.115):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue