Skip to content

Commit

Permalink
aarch64: Fix lowering amounts for shifts
Browse files Browse the repository at this point in the history
This commit addresses two issues:
* A panic when shifting any non i128 type by i128 amounts (#3064)
* Wrong results when lowering shifts with small types (i8, i16)

In these types when shifting for amounts larger than the size of the
type, we would not get the wrapping behaviour that we see on i32 and i64.
This is because in these larger types, the wrapping behaviour is automatically
implemented by using the appropriate instruction, however we do not
have i8 and i16 specific instructions, so we have to manually wrap
the shift amount with an AND instruction.

This issue is also found on x86_64 and s390x, and a separate issue will
be filed for those.

Closes #3064
  • Loading branch information
afonso360 authored and akirilov-arm committed Jul 16, 2021
1 parent 6c3d709 commit db5566d
Show file tree
Hide file tree
Showing 8 changed files with 1,199 additions and 165 deletions.
46 changes: 46 additions & 0 deletions cranelift/codegen/src/isa/aarch64/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ pub(crate) enum ResultRegImmShift {
ImmShift(ImmShift),
}

impl ResultRegImmShift {
pub fn unwrap_reg(self) -> Reg {
match self {
ResultRegImmShift::Reg(r) => r,
_ => panic!("Unwrapped ResultRegImmShift, expected reg, got: {:?}", self),
}
}
}

//============================================================================
// Lowering: convert instruction inputs to forms that we can use.

Expand Down Expand Up @@ -1468,6 +1477,43 @@ pub(crate) fn materialize_bool_result<C: LowerCtx<I = Inst>>(
}
}

pub(crate) fn lower_shift_amt<C: LowerCtx<I = Inst>>(
ctx: &mut C,
amt_input: InsnInput,
dst_ty: Type,
tmp_reg: Writable<Reg>,
) -> ResultRegImmShift {
let amt_ty = ctx.input_ty(amt_input.insn, amt_input.input);

match (dst_ty, amt_ty) {
// When shifting for amounts larger than the size of the type, the CLIF shift
// instructions implement a "wrapping" behaviour, such that an i8 << 8 is
// equivalent to i8 << 0
//
// On i32 and i64 types this matches what the aarch64 spec does, but on smaller
// types (i16, i8) we need to do this manually, so we wrap the shift amount
// with an AND instruction
(I16 | I8, _) => {
// We can ignore the top half of the shift amount register if the type is I128
let amt_reg = put_input_in_regs(ctx, amt_input).regs()[0];
let mask = (ty_bits(dst_ty) - 1) as u64;
ctx.emit(Inst::AluRRImmLogic {
alu_op: ALUOp::And32,
rd: tmp_reg,
rn: amt_reg,
imml: ImmLogic::maybe_from_u64(mask, I32).unwrap(),
});
ResultRegImmShift::Reg(tmp_reg.to_reg())
}
// TODO: We can use immlogic for i128 types here
(I128, _) | (_, I128) => {
// For I128 shifts, we need a register without immlogic
ResultRegImmShift::Reg(put_input_in_regs(ctx, amt_input).regs()[0])
}
_ => put_input_in_reg_immshift(ctx, amt_input, ty_bits(dst_ty)),
}
}

/// This is target-word-size dependent. And it excludes booleans and reftypes.
pub(crate) fn is_valid_atomic_transaction_ty(ty: Type) -> bool {
match ty {
Expand Down
6 changes: 2 additions & 4 deletions cranelift/codegen/src/isa/aarch64/lower_inst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,10 +801,8 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let out_regs = get_output_reg(ctx, outputs[0]);
let ty = ty.unwrap();
if ty == I128 {
// TODO: We can use immlogic here
let src = put_input_in_regs(ctx, inputs[0]);
// We can ignore the top half of the shift amount register
let amt = put_input_in_regs(ctx, inputs[1]).regs()[0];
let amt = lower_shift_amt(ctx, inputs[1], ty, out_regs.regs()[0]).unwrap_reg();

match op {
Opcode::Ishl => emit_shl_i128(ctx, src, out_regs, amt),
Expand All @@ -828,7 +826,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
_ => unreachable!(),
};
let rn = put_input_in_reg(ctx, inputs[0], narrow_mode);
let rm = put_input_in_reg_immshift(ctx, inputs[1], ty_bits(ty));
let rm = lower_shift_amt(ctx, inputs[1], ty, out_regs.regs()[0]);
let alu_op = match op {
Opcode::Ishl => choose_32_64(ty, ALUOp::Lsl32, ALUOp::Lsl64),
Opcode::Ushr => choose_32_64(ty, ALUOp::Lsr32, ALUOp::Lsr64),
Expand Down
36 changes: 8 additions & 28 deletions cranelift/filetests/filetests/isa/aarch64/shift-rotate.clif
Original file line number Diff line number Diff line change
Expand Up @@ -237,25 +237,19 @@ block0(v0: i16, v1: i16):
return v2
}

; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: uxth w0, w0
; check: uxth w0, w0
; nextln: and w1, w1, #15
; nextln: lsr w0, w0, w1
; nextln: ldp fp, lr, [sp], #16
; nextln: ret

function %f11(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = ushr.i8 v0, v1
return v2
}

; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: uxtb w0, w0
; check: uxtb w0, w0
; nextln: and w1, w1, #7
; nextln: lsr w0, w0, w1
; nextln: ldp fp, lr, [sp], #16
; nextln: ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; LSL, variable
Expand Down Expand Up @@ -291,23 +285,17 @@ block0(v0: i16, v1: i16):
return v2
}

; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; check: and w1, w1, #15
; nextln: lsl w0, w0, w1
; nextln: ldp fp, lr, [sp], #16
; nextln: ret

function %f15(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = ishl.i8 v0, v1
return v2
}

; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; check: and w1, w1, #7
; nextln: lsl w0, w0, w1
; nextln: ldp fp, lr, [sp], #16
; nextln: ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ASR, variable
Expand Down Expand Up @@ -343,25 +331,17 @@ block0(v0: i16, v1: i16):
return v2
}

; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sxth w0, w0
; check: and w1, w1, #15
; nextln: asr w0, w0, w1
; nextln: ldp fp, lr, [sp], #16
; nextln: ret

function %f19(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = sshr.i8 v0, v1
return v2
}

; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sxtb w0, w0
; check: and w1, w1, #7
; nextln: asr w0, w0, w1
; nextln: ldp fp, lr, [sp], #16
; nextln: ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; immediate forms
Expand Down
133 changes: 0 additions & 133 deletions cranelift/filetests/filetests/runtests/i128-arithmetic.clif
Original file line number Diff line number Diff line change
Expand Up @@ -71,136 +71,3 @@ block0(v0: i64,v1: i64,v2: i64,v3: i64):
; run: %mul_i128(0x00000000_01234567, 0x89ABCDEF_00000000, 0x00000000_FEDCBA98, 0x76543210_00000000) == [0x0121FA00_23E20B28, 0xE2946058_00000000]
; run: %mul_i128(0xC0FFEEEE_C0FFEEEE, 0xC0FFEEEE_C0FFEEEE, 0xDECAFFFF_DECAFFFF, 0xDECAFFFF_DECAFFFF) == [0xDB6B1E48_19BA1112, 0x5ECD38B5_9D1C2B7E]
; run: %mul_i128(0xC0FFEEEE_C0FFEEEE, 0xC0FFEEEE_C0FFEEEE, 0xDECAFFFF_DECAFFFF, 0xDECAFFFF_DECAFFFF) == [0xDB6B1E48_19BA1112, 0x5ECD38B5_9D1C2B7E]


function %ishl_i128_i8(i64, i64, i8) -> i64, i64 {
block0(v0: i64, v1: i64, v2: i8):
v3 = iconcat v0, v1

v4 = ishl.i128 v3, v2

v5, v6 = isplit v4
return v5, v6
}
; run: %ishl_i128_i8(0x01010101_01010101, 0x01010101_01010101, 2) == [0x04040404_04040404, 0x04040404_04040404]
; run: %ishl_i128_i8(0x01010101_01010101, 0x01010101_01010101, 9) == [0x02020202_02020200, 0x02020202_02020202]
; run: %ishl_i128_i8(0x01010101_01010101, 0xffffffff_ffffffff, 66) == [0x00000000_00000000, 0x04040404_04040404]
; run: %ishl_i128_i8(0x01010101_01010101, 0x01010101_01010101, 0) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ishl_i128_i8(0x01010101_01010101, 0x01010101_01010101, 128) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ishl_i128_i8(0x00000000_00000001, 0x00000000_00000000, 0) == [0x00000000_00000001, 0x00000000_00000000]
; run: %ishl_i128_i8(0x00000000_00000000, 0x00000000_00000001, 0) == [0x00000000_00000000, 0x00000000_00000001]
; run: %ishl_i128_i8(0x12340000_00000000, 0x56780000_00000000, 0) == [0x12340000_00000000, 0x56780000_00000000]
; run: %ishl_i128_i8(0x12340000_00000000, 0x56780000_00000000, 64) == [0x00000000_00000000, 0x12340000_00000000]
; run: %ishl_i128_i8(0x12340000_00000000, 0x56780000_00000000, 32) == [0x00000000_00000000, 0x00000000_12340000]
; run: %ishl_i128_i8(0x01010101_01010101, 0x01010101_01010101, 129) == [0x02020202_02020202, 0x02020202_02020202]
; run: %ishl_i128_i8(0x01010101_01010101, 0x01010101_01010101, 130) == [0x04040404_04040404, 0x04040404_04040404]

function %ishl_i128_i128(i64, i64, i8) -> i64, i64 {
block0(v0: i64, v1: i64, v2: i8):
v3 = iconcat v0, v1
v4 = uextend.i64 v2
v5 = iconcat v4, v4

v6 = ishl.i128 v3, v5

v7, v8 = isplit v6
return v7, v8
}
; run: %ishl_i128_i128(0x01010101_01010101, 0x01010101_01010101, 2) == [0x04040404_04040404, 0x04040404_04040404]
; run: %ishl_i128_i128(0x01010101_01010101, 0x01010101_01010101, 9) == [0x02020202_02020200, 0x02020202_02020202]
; run: %ishl_i128_i128(0x01010101_01010101, 0xffffffff_ffffffff, 66) == [0x00000000_00000000, 0x04040404_04040404]
; run: %ishl_i128_i128(0x01010101_01010101, 0x01010101_01010101, 0) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ishl_i128_i128(0x01010101_01010101, 0x01010101_01010101, 128) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ishl_i128_i128(0x00000000_00000001, 0x00000000_00000000, 0) == [0x00000000_00000001, 0x00000000_00000000]
; run: %ishl_i128_i128(0x00000000_00000000, 0x00000000_00000001, 0) == [0x00000000_00000000, 0x00000000_00000001]
; run: %ishl_i128_i128(0x12340000_00000000, 0x56780000_00000000, 0) == [0x12340000_00000000, 0x56780000_00000000]
; run: %ishl_i128_i128(0x12340000_00000000, 0x56780000_00000000, 64) == [0x00000000_00000000, 0x12340000_00000000]
; run: %ishl_i128_i128(0x12340000_00000000, 0x56780000_00000000, 32) == [0x00000000_00000000, 0x00000000_12340000]
; run: %ishl_i128_i128(0x01010101_01010101, 0x01010101_01010101, 129) == [0x02020202_02020202, 0x02020202_02020202]
; run: %ishl_i128_i128(0x01010101_01010101, 0x01010101_01010101, 130) == [0x04040404_04040404, 0x04040404_04040404]


function %ushr_i128_i8(i64, i64, i8) -> i64, i64 {
block0(v0: i64, v1: i64, v2: i8):
v3 = iconcat v0, v1

v4 = ushr.i128 v3, v2

v5, v6 = isplit v4
return v5, v6
}
; run: %ushr_i128_i8(0x01010101_01010101, 0x01010101_01010101, 2) == [0x40404040_40404040, 0x00404040_40404040]
; run: %ushr_i128_i8(0x01010101_01010101, 0x01010101_01010101, 66) == [0x00404040_40404040, 0x00000000_00000000]
; run: %ushr_i128_i8(0x01010101_01010101, 0x01010101_01010101, 0) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ushr_i128_i8(0x01010101_01010101, 0x01010101_01010101, 128) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ushr_i128_i8(0x00000000_00000001, 0x00000000_00000000, 0) == [0x00000000_00000001, 0x00000000_00000000]
; run: %ushr_i128_i8(0x00000000_00000000, 0x00000000_00000001, 0) == [0x00000000_00000000, 0x00000000_00000001]
; run: %ushr_i128_i8(0x12340000_00000000, 0x56780000_00000000, 0) == [0x12340000_00000000, 0x56780000_00000000]
; run: %ushr_i128_i8(0x12340000_00000000, 0x56780000_00000000, 64) == [0x56780000_00000000, 0x00000000_00000000]
; run: %ushr_i128_i8(0x12340000_00000000, 0x56780000_00000000, 32) == [0x00000000_12340000, 0x00000000_56780000]
; run: %ushr_i128_i8(0x01010101_01010101, 0x01010101_01010101, 129) == [0x80808080_80808080, 0x00808080_80808080]
; run: %ushr_i128_i8(0x01010101_01010101, 0x01010101_01010101, 130) == [0x40404040_40404040, 0x00404040_40404040]

function %ushr_i128_i128(i64, i64, i8) -> i64, i64 {
block0(v0: i64, v1: i64, v2: i8):
v3 = iconcat v0, v1
v4 = uextend.i64 v2
v5 = iconcat v4, v4

v6 = ushr.i128 v3, v5

v7, v8 = isplit v6
return v7, v8
}
; run: %ushr_i128_i128(0x01010101_01010101, 0x01010101_01010101, 2) == [0x40404040_40404040, 0x00404040_40404040]
; run: %ushr_i128_i128(0x01010101_01010101, 0x01010101_01010101, 66) == [0x00404040_40404040, 0x00000000_00000000]
; run: %ushr_i128_i128(0x01010101_01010101, 0x01010101_01010101, 0) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ushr_i128_i128(0x01010101_01010101, 0x01010101_01010101, 128) == [0x01010101_01010101, 0x01010101_01010101]
; run: %ushr_i128_i128(0x00000000_00000001, 0x00000000_00000000, 0) == [0x00000000_00000001, 0x00000000_00000000]
; run: %ushr_i128_i128(0x00000000_00000000, 0x00000000_00000001, 0) == [0x00000000_00000000, 0x00000000_00000001]
; run: %ushr_i128_i128(0x12340000_00000000, 0x56780000_00000000, 0) == [0x12340000_00000000, 0x56780000_00000000]
; run: %ushr_i128_i128(0x12340000_00000000, 0x56780000_00000000, 64) == [0x56780000_00000000, 0x00000000_00000000]
; run: %ushr_i128_i128(0x12340000_00000000, 0x56780000_00000000, 32) == [0x00000000_12340000, 0x00000000_56780000]
; run: %ushr_i128_i128(0x01010101_01010101, 0x01010101_01010101, 129) == [0x80808080_80808080, 0x00808080_80808080]
; run: %ushr_i128_i128(0x01010101_01010101, 0x01010101_01010101, 130) == [0x40404040_40404040, 0x00404040_40404040]


function %sshr_i128_i8(i64, i64, i8) -> i64, i64 {
block0(v0: i64, v1: i64, v2: i8):
v3 = iconcat v0, v1

v4 = sshr.i128 v3, v2

v5, v6 = isplit v4
return v5, v6
}
; run: %sshr_i128_i8(0x01010101_01010101, 0x81010101_01010101, 2) == [0x40404040_40404040, 0xe0404040_40404040]
; run: %sshr_i128_i8(0x00000000_00000000, 0xffffffff_ffffffff, 32) == [0xffffffff_00000000, 0xffffffff_ffffffff]
; run: %sshr_i128_i8(0x80000000_00000000, 0xffffffff_00000000, 32) == [0x00000000_80000000, 0xffffffff_ffffffff]
; run: %sshr_i128_i8(0x12345678_9abcdef0, 0x80101010_10101010, 66) == [0xe0040404_04040404, 0xffffffff_ffffffff]
; run: %sshr_i128_i8(0x00000000_00000000, 0x00000000_00000000, 64) == [0x00000000_00000000, 0x00000000_00000000]
; run: %sshr_i128_i8(0x12345678_9abcdef0, 0x80101010_10101010, 0) == [0x12345678_9abcdef0, 0x80101010_10101010]
; run: %sshr_i128_i8(0x12345678_9abcdef0, 0x80101010_10101010, 128) == [0x12345678_9abcdef0, 0x80101010_10101010]
; run: %sshr_i128_i8(0x01010101_01010101, 0x81010101_01010101, 129) == [0x80808080_80808080, 0xc0808080_80808080]
; run: %sshr_i128_i8(0x01010101_01010101, 0x81010101_01010101, 130) == [0x40404040_40404040, 0xe0404040_40404040]

function %sshr_i128_i128(i64, i64, i8) -> i64, i64 {
block0(v0: i64, v1: i64, v2: i8):
v3 = iconcat v0, v1
v4 = uextend.i64 v2
v5 = iconcat v4, v4

v6 = sshr.i128 v3, v5

v7, v8 = isplit v6
return v7, v8
}
; run: %sshr_i128_i128(0x01010101_01010101, 0x81010101_01010101, 2) == [0x40404040_40404040, 0xe0404040_40404040]
; run: %sshr_i128_i128(0x00000000_00000000, 0xffffffff_ffffffff, 32) == [0xffffffff_00000000, 0xffffffff_ffffffff]
; run: %sshr_i128_i128(0x80000000_00000000, 0xffffffff_00000000, 32) == [0x00000000_80000000, 0xffffffff_ffffffff]
; run: %sshr_i128_i128(0x12345678_9abcdef0, 0x80101010_10101010, 66) == [0xe0040404_04040404, 0xffffffff_ffffffff]
; run: %sshr_i128_i128(0x00000000_00000000, 0x00000000_00000000, 64) == [0x00000000_00000000, 0x00000000_00000000]
; run: %sshr_i128_i128(0x12345678_9abcdef0, 0x80101010_10101010, 0) == [0x12345678_9abcdef0, 0x80101010_10101010]
; run: %sshr_i128_i128(0x12345678_9abcdef0, 0x80101010_10101010, 128) == [0x12345678_9abcdef0, 0x80101010_10101010]
; run: %sshr_i128_i128(0x01010101_01010101, 0x81010101_01010101, 129) == [0x80808080_80808080, 0xc0808080_80808080]
; run: %sshr_i128_i128(0x01010101_01010101, 0x81010101_01010101, 130) == [0x40404040_40404040, 0xe0404040_40404040]
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
test run
target aarch64

; TODO: Merge this with the main i128-shifts file when x86_64 passes these.

function %ishl_i16_i128(i16, i64, i64) -> i16 {
block0(v0: i16, v1: i64, v2: i64):
v3 = iconcat v1, v2
v4 = ishl.i16 v0, v3
return v4
}
; run: %ishl_i16_i128(0x0000, 0, 0) == 0x0000
; run: %ishl_i16_i128(0x0000, 1, 0) == 0x0000
; run: %ishl_i16_i128(0x000f, 0, 4) == 0x000f
; run: %ishl_i16_i128(0x000f, 4, 0) == 0x00f0
; run: %ishl_i16_i128(0x0004, 16, 0) == 0x0004
; run: %ishl_i16_i128(0x0004, 17, 0) == 0x0008
; run: %ishl_i16_i128(0x0004, 18, 1) == 0x0010

function %ishl_i8_i128(i8, i64, i64) -> i8 {
block0(v0: i8, v1: i64, v2: i64):
v3 = iconcat v1, v2
v4 = ishl.i8 v0, v3
return v4
}
; run: %ishl_i8_i128(0x00, 0, 0) == 0x00
; run: %ishl_i8_i128(0x00, 1, 0) == 0x00
; run: %ishl_i8_i128(0x0f, 0, 4) == 0x0f
; run: %ishl_i8_i128(0x0f, 4, 0) == 0xf0
; run: %ishl_i8_i128(0x04, 8, 0) == 0x04
; run: %ishl_i8_i128(0x04, 9, 0) == 0x08
; run: %ishl_i8_i128(0x04, 10, 1) == 0x10


function %ushr_i16_i128(i16, i64, i64) -> i16 {
block0(v0: i16, v1: i64, v2: i64):
v3 = iconcat v1, v2
v4 = ushr.i16 v0, v3
return v4
}
; run: %ushr_i16_i128(0x1000, 0, 0) == 0x1000
; run: %ushr_i16_i128(0x1000, 1, 0) == 0x0800
; run: %ushr_i16_i128(0xf000, 0, 4) == 0xf000
; run: %ushr_i16_i128(0xf000, 4, 0) == 0x0f00
; run: %ushr_i16_i128(0x4000, 16, 0) == 0x4000
; run: %ushr_i16_i128(0x4000, 17, 0) == 0x2000
; run: %ushr_i16_i128(0x4000, 18, 1) == 0x1000

function %ushr_i8_i128(i8, i64, i64) -> i8 {
block0(v0: i8, v1: i64, v2: i64):
v3 = iconcat v1, v2
v4 = ushr.i8 v0, v3
return v4
}
; run: %ushr_i8_i128(0x10, 0, 0) == 0x10
; run: %ushr_i8_i128(0x10, 1, 0) == 0x08
; run: %ushr_i8_i128(0xf0, 0, 4) == 0xf0
; run: %ushr_i8_i128(0xf0, 4, 0) == 0x0f
; run: %ushr_i8_i128(0x40, 8, 0) == 0x40
; run: %ushr_i8_i128(0x40, 9, 0) == 0x20
; run: %ushr_i8_i128(0x40, 10, 1) == 0x10


function %sshr_i16_i128(i16, i64, i64) -> i16 {
block0(v0: i16, v1: i64, v2: i64):
v3 = iconcat v1, v2
v4 = sshr.i16 v0, v3
return v4
}
; run: %sshr_i16_i128(0x8000, 0, 0) == 0x8000
; run: %sshr_i16_i128(0x8000, 1, 0) == 0xC000
; run: %sshr_i16_i128(0xf000, 0, 4) == 0xf000
; run: %sshr_i16_i128(0xf000, 4, 0) == 0xff00
; run: %sshr_i16_i128(0x4000, 16, 0) == 0x4000
; run: %sshr_i16_i128(0x4000, 17, 0) == 0x2000
; run: %sshr_i16_i128(0x4000, 18, 1) == 0x1000

function %sshr_i8_i128(i8, i64, i64) -> i8 {
block0(v0: i8, v1: i64, v2: i64):
v3 = iconcat v1, v2
v4 = sshr.i8 v0, v3
return v4
}
; run: %sshr_i8_i128(0x80, 0, 0) == 0x80
; run: %sshr_i8_i128(0x80, 1, 0) == 0xC0
; run: %sshr_i8_i128(0xf0, 0, 4) == 0xf0
; run: %sshr_i8_i128(0xf0, 4, 0) == 0xff
; run: %sshr_i8_i128(0x40, 8, 0) == 0x40
; run: %sshr_i8_i128(0x40, 9, 0) == 0x20
; run: %sshr_i8_i128(0x40, 10, 1) == 0x10
Loading

0 comments on commit db5566d

Please sign in to comment.