Commit
6ad41bcc49
changed how frem is expanded during legalization and it
broke WebAssembly but we were missing test coverage. We want to maintain
our previous behavior of unrolling vectors and using a libcall to
implement scalar frem. I'm not sure why this now has to be different
(in ISelLowering) from other libcalls like fsin which work the same way
in the end, but this code does accurately describe what we want.
Fixes: https://github.com/emscripten-core/emscripten/issues/25991
263 lines
10 KiB
LLVM
263 lines
10 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
|
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -wasm-disable-explicit-locals | FileCheck %s
|
|
|
|
; Test a subset of compiler-rt/libm libcalls expected to be emitted by the wasm backend
|
|
|
|
target triple = "wasm32-unknown-unknown"
|
|
|
|
declare fp128 @llvm.sqrt.f128(fp128)
|
|
declare fp128 @llvm.floor.f128(fp128)
|
|
declare fp128 @llvm.trunc.f128(fp128)
|
|
declare fp128 @llvm.nearbyint.f128(fp128)
|
|
declare fp128 @llvm.pow.f128(fp128, fp128)
|
|
declare fp128 @llvm.powi.f128.i32(fp128, i32)
|
|
|
|
declare double @llvm.tan.f64(double)
|
|
declare double @llvm.cos.f64(double)
|
|
declare double @llvm.log10.f64(double)
|
|
declare double @llvm.pow.f64(double, double)
|
|
declare double @llvm.powi.f64.i32(double, i32)
|
|
declare double @llvm.log.f64(double)
|
|
declare double @llvm.exp.f64(double)
|
|
declare double @llvm.exp10.f64(double)
|
|
declare double @llvm.ldexp.f64.i32(double, i32)
|
|
declare {double, i32} @llvm.frexp.f64.i32(double)
|
|
declare i32 @llvm.lround(double)
|
|
declare {double, double} @llvm.modf.f64(double)
|
|
|
|
declare void @escape_value(i32)
|
|
|
|
define fp128 @fp128libcalls(fp128 %x, fp128 %y, i32 %z) {
|
|
; compiler-rt call
|
|
; CHECK-LABEL: fp128libcalls:
|
|
; CHECK: .functype fp128libcalls (i32, i64, i64, i64, i64, i32) -> ()
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: global.get $push20=, __stack_pointer
|
|
; CHECK-NEXT: i32.const $push21=, 160
|
|
; CHECK-NEXT: i32.sub $push43=, $pop20, $pop21
|
|
; CHECK-NEXT: local.tee $push42=, $6=, $pop43
|
|
; CHECK-NEXT: global.set __stack_pointer, $pop42
|
|
; CHECK-NEXT: i32.const $push40=, 144
|
|
; CHECK-NEXT: i32.add $push41=, $6, $pop40
|
|
; CHECK-NEXT: call __addtf3, $pop41, $1, $2, $3, $4
|
|
; CHECK-NEXT: i32.const $push38=, 128
|
|
; CHECK-NEXT: i32.add $push39=, $6, $pop38
|
|
; CHECK-NEXT: i64.load $push1=, 144($6)
|
|
; CHECK-NEXT: i64.load $push0=, 152($6)
|
|
; CHECK-NEXT: call __multf3, $pop39, $pop1, $pop0, $3, $4
|
|
; CHECK-NEXT: i32.const $push36=, 112
|
|
; CHECK-NEXT: i32.add $push37=, $6, $pop36
|
|
; CHECK-NEXT: i64.load $push3=, 128($6)
|
|
; CHECK-NEXT: i64.load $push2=, 136($6)
|
|
; CHECK-NEXT: call __divtf3, $pop37, $pop3, $pop2, $3, $4
|
|
; CHECK-NEXT: i32.const $push34=, 96
|
|
; CHECK-NEXT: i32.add $push35=, $6, $pop34
|
|
; CHECK-NEXT: i64.load $push5=, 112($6)
|
|
; CHECK-NEXT: i64.load $push4=, 120($6)
|
|
; CHECK-NEXT: call fmodl, $pop35, $pop5, $pop4, $3, $4
|
|
; CHECK-NEXT: i32.const $push32=, 80
|
|
; CHECK-NEXT: i32.add $push33=, $6, $pop32
|
|
; CHECK-NEXT: i64.load $push7=, 96($6)
|
|
; CHECK-NEXT: i64.load $push6=, 104($6)
|
|
; CHECK-NEXT: call sqrtl, $pop33, $pop7, $pop6
|
|
; CHECK-NEXT: i32.const $push30=, 64
|
|
; CHECK-NEXT: i32.add $push31=, $6, $pop30
|
|
; CHECK-NEXT: i64.load $push9=, 80($6)
|
|
; CHECK-NEXT: i64.load $push8=, 88($6)
|
|
; CHECK-NEXT: call floorl, $pop31, $pop9, $pop8
|
|
; CHECK-NEXT: i32.const $push28=, 48
|
|
; CHECK-NEXT: i32.add $push29=, $6, $pop28
|
|
; CHECK-NEXT: i64.load $push11=, 64($6)
|
|
; CHECK-NEXT: i64.load $push10=, 72($6)
|
|
; CHECK-NEXT: call powl, $pop29, $pop11, $pop10, $3, $4
|
|
; CHECK-NEXT: i32.const $push26=, 32
|
|
; CHECK-NEXT: i32.add $push27=, $6, $pop26
|
|
; CHECK-NEXT: i64.load $push13=, 48($6)
|
|
; CHECK-NEXT: i64.load $push12=, 56($6)
|
|
; CHECK-NEXT: call __powitf2, $pop27, $pop13, $pop12, $5
|
|
; CHECK-NEXT: i32.const $push24=, 16
|
|
; CHECK-NEXT: i32.add $push25=, $6, $pop24
|
|
; CHECK-NEXT: i64.load $push15=, 32($6)
|
|
; CHECK-NEXT: i64.load $push14=, 40($6)
|
|
; CHECK-NEXT: call truncl, $pop25, $pop15, $pop14
|
|
; CHECK-NEXT: i64.load $push17=, 16($6)
|
|
; CHECK-NEXT: i64.load $push16=, 24($6)
|
|
; CHECK-NEXT: call nearbyintl, $6, $pop17, $pop16
|
|
; CHECK-NEXT: i64.load $push18=, 8($6)
|
|
; CHECK-NEXT: i64.store 8($0), $pop18
|
|
; CHECK-NEXT: i64.load $push19=, 0($6)
|
|
; CHECK-NEXT: i64.store 0($0), $pop19
|
|
; CHECK-NEXT: i32.const $push22=, 160
|
|
; CHECK-NEXT: i32.add $push23=, $6, $pop22
|
|
; CHECK-NEXT: global.set __stack_pointer, $pop23
|
|
; CHECK-NEXT: return
|
|
%a = fadd fp128 %x, %y
|
|
%b = fmul fp128 %a, %y
|
|
%c = fdiv fp128 %b, %y
|
|
%d = frem fp128 %c, %y
|
|
; libm calls
|
|
%e = call fp128 @llvm.sqrt.f128(fp128 %d)
|
|
%f = call fp128 @llvm.floor.f128(fp128 %e)
|
|
%g = call fp128 @llvm.pow.f128(fp128 %f, fp128 %y)
|
|
%h = call fp128 @llvm.powi.f128.i32(fp128 %g, i32 %z)
|
|
%i = call fp128 @llvm.trunc.f128(fp128 %h)
|
|
%j = call fp128 @llvm.nearbyint.f128(fp128 %i)
|
|
ret fp128 %j
|
|
}
|
|
|
|
define i128 @i128libcalls(i128 %x, i128 %y) {
|
|
; Basic ops should be expanded
|
|
; CHECK-LABEL: i128libcalls:
|
|
; CHECK: .functype i128libcalls (i32, i64, i64, i64, i64) -> ()
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: global.get $push8=, __stack_pointer
|
|
; CHECK-NEXT: i32.const $push9=, 32
|
|
; CHECK-NEXT: i32.sub $push17=, $pop8, $pop9
|
|
; CHECK-NEXT: local.tee $push16=, $6=, $pop17
|
|
; CHECK-NEXT: global.set __stack_pointer, $pop16
|
|
; CHECK-NEXT: i32.const $push12=, 16
|
|
; CHECK-NEXT: i32.add $push13=, $6, $pop12
|
|
; CHECK-NEXT: i64.add $push15=, $1, $3
|
|
; CHECK-NEXT: local.tee $push14=, $5=, $pop15
|
|
; CHECK-NEXT: i64.add $push0=, $2, $4
|
|
; CHECK-NEXT: i64.lt_u $push1=, $5, $1
|
|
; CHECK-NEXT: i64.extend_i32_u $push2=, $pop1
|
|
; CHECK-NEXT: i64.add $push3=, $pop0, $pop2
|
|
; CHECK-NEXT: call __multi3, $pop13, $pop14, $pop3, $3, $4
|
|
; CHECK-NEXT: i64.load $push5=, 16($6)
|
|
; CHECK-NEXT: i64.load $push4=, 24($6)
|
|
; CHECK-NEXT: call __umodti3, $6, $pop5, $pop4, $3, $4
|
|
; CHECK-NEXT: i64.load $push6=, 8($6)
|
|
; CHECK-NEXT: i64.store 8($0), $pop6
|
|
; CHECK-NEXT: i64.load $push7=, 0($6)
|
|
; CHECK-NEXT: i64.store 0($0), $pop7
|
|
; CHECK-NEXT: i32.const $push10=, 32
|
|
; CHECK-NEXT: i32.add $push11=, $6, $pop10
|
|
; CHECK-NEXT: global.set __stack_pointer, $pop11
|
|
; CHECK-NEXT: return
|
|
%a = add i128 %x, %y
|
|
%b = mul i128 %a, %y
|
|
%c = urem i128 %b, %y
|
|
ret i128 %c
|
|
}
|
|
|
|
define double @f64libcalls(double %x, double %y, i32 %z) {
|
|
; CHECK-LABEL: f64libcalls:
|
|
; CHECK: .functype f64libcalls (f64, f64, i32) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: global.get $push12=, __stack_pointer
|
|
; CHECK-NEXT: i32.const $push13=, 16
|
|
; CHECK-NEXT: i32.sub $push23=, $pop12, $pop13
|
|
; CHECK-NEXT: local.tee $push22=, $3=, $pop23
|
|
; CHECK-NEXT: global.set __stack_pointer, $pop22
|
|
; CHECK-NEXT: call $push0=, tan, $0
|
|
; CHECK-NEXT: call $push1=, cos, $pop0
|
|
; CHECK-NEXT: call $push2=, log10, $pop1
|
|
; CHECK-NEXT: call $push3=, pow, $pop2, $1
|
|
; CHECK-NEXT: call $push4=, __powidf2, $pop3, $2
|
|
; CHECK-NEXT: call $push5=, log, $pop4
|
|
; CHECK-NEXT: call $push6=, exp, $pop5
|
|
; CHECK-NEXT: call $push7=, exp10, $pop6
|
|
; CHECK-NEXT: call $push21=, cbrt, $pop7
|
|
; CHECK-NEXT: local.tee $push20=, $1=, $pop21
|
|
; CHECK-NEXT: call $push8=, lround, $pop20
|
|
; CHECK-NEXT: call $push9=, ldexp, $0, $pop8
|
|
; CHECK-NEXT: call $push10=, fmod, $pop9, $1
|
|
; CHECK-NEXT: i32.const $push18=, 4
|
|
; CHECK-NEXT: i32.add $push19=, $3, $pop18
|
|
; CHECK-NEXT: call $0=, frexp, $pop10, $pop19
|
|
; CHECK-NEXT: i32.load $push11=, 4($3)
|
|
; CHECK-NEXT: call escape_value, $pop11
|
|
; CHECK-NEXT: i32.const $push16=, 8
|
|
; CHECK-NEXT: i32.add $push17=, $3, $pop16
|
|
; CHECK-NEXT: call $0=, modf, $0, $pop17
|
|
; CHECK-NEXT: i32.const $push14=, 16
|
|
; CHECK-NEXT: i32.add $push15=, $3, $pop14
|
|
; CHECK-NEXT: global.set __stack_pointer, $pop15
|
|
; CHECK-NEXT: return $0
|
|
|
|
|
|
%k = call double @llvm.tan.f64(double %x)
|
|
%a = call double @llvm.cos.f64(double %k)
|
|
%b = call double @llvm.log10.f64(double %a)
|
|
%c = call double @llvm.pow.f64(double %b, double %y)
|
|
%d = call double @llvm.powi.f64.i32(double %c, i32 %z)
|
|
%e = call double @llvm.log.f64(double %d)
|
|
%f = call double @llvm.exp.f64(double %e)
|
|
%g = call double @llvm.exp10.f64(double %f)
|
|
%h = call fast double @llvm.pow.f64(double %g, double 0x3FD5555555555555)
|
|
%i = call i32 @llvm.lround(double %h)
|
|
%j = call double @llvm.ldexp.f64.i32(double %x, i32 %i);
|
|
%l = frem double %j, %h;
|
|
%result = call {double, i32} @llvm.frexp.f64.i32(double %l)
|
|
%result.0 = extractvalue { double, i32 } %result, 0
|
|
%result.1 = extractvalue { double, i32 } %result, 1
|
|
%resultModf = call {double, double} @llvm.modf.f64(double %result.0)
|
|
%resultModf.0 = extractvalue { double, double } %resultModf, 0
|
|
call void @escape_value(i32 %result.1)
|
|
ret double %resultModf.0
|
|
}
|
|
|
|
; fcmp ord and unord (RTLIB::O_F32 / RTLIB::UO_F32 etc) are a special case (see
|
|
; comment in WebAssemblyRunimeLibcallSignatures.cpp) so check them separately.
|
|
; no libcalls are needed for f32 and f64
|
|
|
|
define i1 @unordd(double %x, double %y) {
|
|
; CHECK-LABEL: unordd:
|
|
; CHECK: .functype unordd (f64, f64) -> (i32)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: f64.ne $push4=, $0, $0
|
|
; CHECK-NEXT: f64.ne $push3=, $1, $1
|
|
; CHECK-NEXT: i32.or $push5=, $pop4, $pop3
|
|
; CHECK-NEXT: f64.eq $push1=, $0, $0
|
|
; CHECK-NEXT: f64.eq $push0=, $1, $1
|
|
; CHECK-NEXT: i32.and $push2=, $pop1, $pop0
|
|
; CHECK-NEXT: i32.xor $push6=, $pop5, $pop2
|
|
; CHECK-NEXT: return $pop6
|
|
%a = fcmp uno double %x, %y
|
|
%b = fcmp ord double %x, %y
|
|
%c = xor i1 %a, %b
|
|
ret i1 %c
|
|
}
|
|
|
|
define i1 @unordf(float %x, float %y) {
|
|
; CHECK-LABEL: unordf:
|
|
; CHECK: .functype unordf (f32, f32) -> (i32)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: f32.ne $push4=, $0, $0
|
|
; CHECK-NEXT: f32.ne $push3=, $1, $1
|
|
; CHECK-NEXT: i32.or $push5=, $pop4, $pop3
|
|
; CHECK-NEXT: f32.eq $push1=, $0, $0
|
|
; CHECK-NEXT: f32.eq $push0=, $1, $1
|
|
; CHECK-NEXT: i32.and $push2=, $pop1, $pop0
|
|
; CHECK-NEXT: i32.xor $push6=, $pop5, $pop2
|
|
; CHECK-NEXT: return $pop6
|
|
%a = fcmp uno float %x, %y
|
|
%b = fcmp ord float %x, %y
|
|
%c = xor i1 %a, %b
|
|
ret i1 %c
|
|
}
|
|
|
|
define i1 @unordt(fp128 %x, fp128 %y) {
|
|
; CHECK-LABEL: unordt:
|
|
; CHECK: .functype unordt (i64, i64, i64, i64) -> (i32)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: call $push1=, __unordtf2, $0, $1, $2, $3
|
|
; CHECK-NEXT: i32.const $push0=, 0
|
|
; CHECK-NEXT: i32.ne $push2=, $pop1, $pop0
|
|
; CHECK-NEXT: return $pop2
|
|
%a = fcmp uno fp128 %x, %y
|
|
ret i1 %a
|
|
}
|
|
|
|
define i1 @ordt(fp128 %x, fp128 %y) {
|
|
; CHECK-LABEL: ordt:
|
|
; CHECK: .functype ordt (i64, i64, i64, i64) -> (i32)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: call $push0=, __unordtf2, $0, $1, $2, $3
|
|
; CHECK-NEXT: i32.eqz $push1=, $pop0
|
|
; CHECK-NEXT: return $pop1
|
|
%a = fcmp ord fp128 %x, %y
|
|
ret i1 %a
|
|
}
|