Skip to content

Commit 57babd0

Browse files
committed
[WebAssembly] Change half to use soft promotion rather than PromoteFloat
The default `half` legalization, which Wasm currently uses, does not respect IEEE conventions: for example, casting to bits may invoke a lossy libcall, meaning soft float operations cannot be correctly implemented. Change to the soft promotion legalization which passes `f16` as an `i16` and treats each `half` operation as an individual f16->f32->libcall->f32->f16 sequence. Of note in the test updates are that `from_bits` and `to_bits` are now libcall-free, and that chained operations now round back to `f16` after each step. Fixes the wasm portion of #97981 Fixes the wasm portion of #97975 Fixes: #96438 Fixes: #96438
1 parent 7cbd5af commit 57babd0

File tree

5 files changed

+287
-424
lines changed

5 files changed

+287
-424
lines changed

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class WebAssemblyTargetLowering final : public TargetLowering {
4040
MVT getPointerTy(const DataLayout &DL, uint32_t AS = 0) const override;
4141
MVT getPointerMemTy(const DataLayout &DL, uint32_t AS = 0) const override;
4242

43+
bool softPromoteHalfType() const override { return true; }
44+
4345
private:
4446
/// Keep a pointer to the WebAssemblySubtarget around so that we can make the
4547
/// right decision when generating code for different targets.

llvm/test/CodeGen/WebAssembly/f16.ll

Lines changed: 88 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,23 @@ target triple = "wasm32-unknown-unknown"
1010

1111
define void @store(half %x, ptr %p) nounwind {
1212
; ALL-LABEL: store:
13-
; ALL: .functype store (f32, i32) -> ()
13+
; ALL: .functype store (i32, i32) -> ()
1414
; ALL-NEXT: # %bb.0:
15-
; ALL-NEXT: local.get $push2=, 1
16-
; ALL-NEXT: local.get $push1=, 0
17-
; ALL-NEXT: call $push0=, __truncsfhf2, $pop1
18-
; ALL-NEXT: i32.store16 0($pop2), $pop0
15+
; ALL-NEXT: local.get $push1=, 1
16+
; ALL-NEXT: local.get $push0=, 0
17+
; ALL-NEXT: i32.store16 0($pop1), $pop0
1918
; ALL-NEXT: return
2019
store half %x, ptr %p
2120
ret void
2221
}
2322

2423
define half @return(ptr %p) nounwind {
2524
; ALL-LABEL: return:
26-
; ALL: .functype return (i32) -> (f32)
25+
; ALL: .functype return (i32) -> (i32)
2726
; ALL-NEXT: # %bb.0:
28-
; ALL-NEXT: local.get $push2=, 0
29-
; ALL-NEXT: i32.load16_u $push0=, 0($pop2)
30-
; ALL-NEXT: call $push1=, __extendhfsf2, $pop0
31-
; ALL-NEXT: return $pop1
27+
; ALL-NEXT: local.get $push1=, 0
28+
; ALL-NEXT: i32.load16_u $push0=, 0($pop1)
29+
; ALL-NEXT: return $pop0
3230
%r = load half, ptr %p
3331
ret half %r
3432
}
@@ -80,50 +78,28 @@ define dso_local float @loadf(ptr nocapture readonly %a) local_unnamed_addr noun
8078
}
8179

8280
define dso_local void @stored(ptr nocapture %a, double %b) local_unnamed_addr nounwind {
83-
; DEFISEL-LABEL: stored:
84-
; DEFISEL: .functype stored (i32, f64) -> ()
85-
; DEFISEL-NEXT: # %bb.0:
86-
; DEFISEL-NEXT: local.get $push2=, 0
87-
; DEFISEL-NEXT: local.get $push1=, 1
88-
; DEFISEL-NEXT: call $push0=, __truncdfhf2, $pop1
89-
; DEFISEL-NEXT: i32.store16 0($pop2), $pop0
90-
; DEFISEL-NEXT: return
91-
;
92-
; FASTISEL-LABEL: stored:
93-
; FASTISEL: .functype stored (i32, f64) -> ()
94-
; FASTISEL-NEXT: # %bb.0:
95-
; FASTISEL-NEXT: local.get $push4=, 0
96-
; FASTISEL-NEXT: local.get $push3=, 1
97-
; FASTISEL-NEXT: call $push2=, __truncdfhf2, $pop3
98-
; FASTISEL-NEXT: i32.const $push1=, 65535
99-
; FASTISEL-NEXT: i32.and $push0=, $pop2, $pop1
100-
; FASTISEL-NEXT: i32.store16 0($pop4), $pop0
101-
; FASTISEL-NEXT: return
81+
; ALL-LABEL: stored:
82+
; ALL: .functype stored (i32, f64) -> ()
83+
; ALL-NEXT: # %bb.0:
84+
; ALL-NEXT: local.get $push2=, 0
85+
; ALL-NEXT: local.get $push1=, 1
86+
; ALL-NEXT: call $push0=, __truncdfhf2, $pop1
87+
; ALL-NEXT: i32.store16 0($pop2), $pop0
88+
; ALL-NEXT: return
10289
%x = tail call i16 @llvm.convert.to.fp16.f64(double %b)
10390
store i16 %x, ptr %a, align 2
10491
ret void
10592
}
10693

10794
define dso_local void @storef(ptr nocapture %a, float %b) local_unnamed_addr nounwind {
108-
; DEFISEL-LABEL: storef:
109-
; DEFISEL: .functype storef (i32, f32) -> ()
110-
; DEFISEL-NEXT: # %bb.0:
111-
; DEFISEL-NEXT: local.get $push2=, 0
112-
; DEFISEL-NEXT: local.get $push1=, 1
113-
; DEFISEL-NEXT: call $push0=, __truncsfhf2, $pop1
114-
; DEFISEL-NEXT: i32.store16 0($pop2), $pop0
115-
; DEFISEL-NEXT: return
116-
;
117-
; FASTISEL-LABEL: storef:
118-
; FASTISEL: .functype storef (i32, f32) -> ()
119-
; FASTISEL-NEXT: # %bb.0:
120-
; FASTISEL-NEXT: local.get $push4=, 0
121-
; FASTISEL-NEXT: local.get $push3=, 1
122-
; FASTISEL-NEXT: call $push2=, __truncsfhf2, $pop3
123-
; FASTISEL-NEXT: i32.const $push1=, 65535
124-
; FASTISEL-NEXT: i32.and $push0=, $pop2, $pop1
125-
; FASTISEL-NEXT: i32.store16 0($pop4), $pop0
126-
; FASTISEL-NEXT: return
95+
; ALL-LABEL: storef:
96+
; ALL: .functype storef (i32, f32) -> ()
97+
; ALL-NEXT: # %bb.0:
98+
; ALL-NEXT: local.get $push2=, 0
99+
; ALL-NEXT: local.get $push1=, 1
100+
; ALL-NEXT: call $push0=, __truncsfhf2, $pop1
101+
; ALL-NEXT: i32.store16 0($pop2), $pop0
102+
; ALL-NEXT: return
127103
%x = tail call i16 @llvm.convert.to.fp16.f32(float %b)
128104
store i16 %x, ptr %a, align 2
129105
ret void
@@ -170,33 +146,20 @@ define void @test_bitcast_to_half(ptr %addr, i16 %in) nounwind {
170146

171147
define half @from_bits(i16 %x) nounwind {
172148
; ALL-LABEL: from_bits:
173-
; ALL: .functype from_bits (i32) -> (f32)
149+
; ALL: .functype from_bits (i32) -> (i32)
174150
; ALL-NEXT: # %bb.0:
175-
; ALL-NEXT: local.get $push1=, 0
176-
; ALL-NEXT: call $push0=, __extendhfsf2, $pop1
151+
; ALL-NEXT: local.get $push0=, 0
177152
; ALL-NEXT: return $pop0
178153
%res = bitcast i16 %x to half
179154
ret half %res
180155
}
181156

182157
define i16 @to_bits(half %x) nounwind {
183-
; DEFISEL-LABEL: to_bits:
184-
; DEFISEL: .functype to_bits (f32) -> (i32)
185-
; DEFISEL-NEXT: # %bb.0:
186-
; DEFISEL-NEXT: local.get $push3=, 0
187-
; DEFISEL-NEXT: call $push1=, __truncsfhf2, $pop3
188-
; DEFISEL-NEXT: i32.const $push0=, 65535
189-
; DEFISEL-NEXT: i32.and $push2=, $pop1, $pop0
190-
; DEFISEL-NEXT: return $pop2
191-
;
192-
; FASTISEL-LABEL: to_bits:
193-
; FASTISEL: .functype to_bits (f32) -> (i32)
194-
; FASTISEL-NEXT: # %bb.0:
195-
; FASTISEL-NEXT: local.get $push3=, 0
196-
; FASTISEL-NEXT: call $push2=, __truncsfhf2, $pop3
197-
; FASTISEL-NEXT: i32.const $push1=, 65535
198-
; FASTISEL-NEXT: i32.and $push0=, $pop2, $pop1
199-
; FASTISEL-NEXT: return $pop0
158+
; ALL-LABEL: to_bits:
159+
; ALL: .functype to_bits (i32) -> (i32)
160+
; ALL-NEXT: # %bb.0:
161+
; ALL-NEXT: local.get $push0=, 0
162+
; ALL-NEXT: return $pop0
200163
%res = bitcast half %x to i16
201164
ret i16 %res
202165
}
@@ -559,27 +522,35 @@ define float @test_sitofp_fadd_i32(i32 %a, ptr %b) nounwind {
559522
; DEFISEL-LABEL: test_sitofp_fadd_i32:
560523
; DEFISEL: .functype test_sitofp_fadd_i32 (i32, i32) -> (f32)
561524
; DEFISEL-NEXT: # %bb.0:
562-
; DEFISEL-NEXT: local.get $push6=, 1
563-
; DEFISEL-NEXT: i32.load16_u $push1=, 0($pop6)
525+
; DEFISEL-NEXT: local.get $push8=, 1
526+
; DEFISEL-NEXT: i32.load16_u $push7=, 0($pop8)
527+
; DEFISEL-NEXT: local.set 1, $pop7
528+
; DEFISEL-NEXT: local.get $push9=, 0
529+
; DEFISEL-NEXT: f32.convert_i32_s $push0=, $pop9
530+
; DEFISEL-NEXT: call $push1=, __truncsfhf2, $pop0
564531
; DEFISEL-NEXT: call $push2=, __extendhfsf2, $pop1
565-
; DEFISEL-NEXT: local.get $push7=, 0
566-
; DEFISEL-NEXT: f32.convert_i32_s $push0=, $pop7
567-
; DEFISEL-NEXT: call $push3=, __truncsfhf2, $pop0
568-
; DEFISEL-NEXT: call $push4=, __extendhfsf2, $pop3
569-
; DEFISEL-NEXT: f32.add $push5=, $pop2, $pop4
570-
; DEFISEL-NEXT: return $pop5
532+
; DEFISEL-NEXT: local.get $push10=, 1
533+
; DEFISEL-NEXT: call $push3=, __extendhfsf2, $pop10
534+
; DEFISEL-NEXT: f32.add $push4=, $pop2, $pop3
535+
; DEFISEL-NEXT: call $push5=, __truncsfhf2, $pop4
536+
; DEFISEL-NEXT: call $push6=, __extendhfsf2, $pop5
537+
; DEFISEL-NEXT: return $pop6
571538
;
572539
; FASTISEL-LABEL: test_sitofp_fadd_i32:
573540
; FASTISEL: .functype test_sitofp_fadd_i32 (i32, i32) -> (f32)
574541
; FASTISEL-NEXT: # %bb.0:
575-
; FASTISEL-NEXT: local.get $push6=, 1
576-
; FASTISEL-NEXT: i32.load16_u $push2=, 0($pop6)
542+
; FASTISEL-NEXT: local.get $push8=, 1
543+
; FASTISEL-NEXT: i32.load16_u $push7=, 0($pop8)
544+
; FASTISEL-NEXT: local.set 1, $pop7
545+
; FASTISEL-NEXT: local.get $push9=, 0
546+
; FASTISEL-NEXT: f32.convert_i32_s $push1=, $pop9
547+
; FASTISEL-NEXT: call $push2=, __truncsfhf2, $pop1
577548
; FASTISEL-NEXT: call $push3=, __extendhfsf2, $pop2
578-
; FASTISEL-NEXT: local.get $push7=, 0
579-
; FASTISEL-NEXT: f32.convert_i32_s $push1=, $pop7
580-
; FASTISEL-NEXT: call $push4=, __truncsfhf2, $pop1
581-
; FASTISEL-NEXT: call $push5=, __extendhfsf2, $pop4
582-
; FASTISEL-NEXT: f32.add $push0=, $pop3, $pop5
549+
; FASTISEL-NEXT: local.get $push10=, 1
550+
; FASTISEL-NEXT: call $push4=, __extendhfsf2, $pop10
551+
; FASTISEL-NEXT: f32.add $push5=, $pop3, $pop4
552+
; FASTISEL-NEXT: call $push6=, __truncsfhf2, $pop5
553+
; FASTISEL-NEXT: call $push0=, __extendhfsf2, $pop6
583554
; FASTISEL-NEXT: return $pop0
584555
%tmp0 = load half, ptr %b
585556
%tmp1 = sitofp i32 %a to half
@@ -590,17 +561,20 @@ define float @test_sitofp_fadd_i32(i32 %a, ptr %b) nounwind {
590561

591562
define half @chained_fp_ops(half %x) {
592563
; ALL-LABEL: chained_fp_ops:
593-
; ALL: .functype chained_fp_ops (f32) -> (f32)
564+
; ALL: .functype chained_fp_ops (i32) -> (i32)
565+
; ALL-NEXT: .local f32
594566
; ALL-NEXT: # %bb.0: # %start
595-
; ALL-NEXT: local.get $push6=, 0
596-
; ALL-NEXT: call $push0=, __truncsfhf2, $pop6
597-
; ALL-NEXT: call $push5=, __extendhfsf2, $pop0
598-
; ALL-NEXT: local.tee $push4=, 0, $pop5
599-
; ALL-NEXT: local.get $push7=, 0
600-
; ALL-NEXT: f32.add $push1=, $pop4, $pop7
601-
; ALL-NEXT: f32.const $push2=, 0x1p-1
602-
; ALL-NEXT: f32.mul $push3=, $pop1, $pop2
603-
; ALL-NEXT: return $pop3
567+
; ALL-NEXT: local.get $push8=, 0
568+
; ALL-NEXT: call $push7=, __extendhfsf2, $pop8
569+
; ALL-NEXT: local.tee $push6=, 1, $pop7
570+
; ALL-NEXT: local.get $push9=, 1
571+
; ALL-NEXT: f32.add $push0=, $pop6, $pop9
572+
; ALL-NEXT: call $push2=, __truncsfhf2, $pop0
573+
; ALL-NEXT: call $push3=, __extendhfsf2, $pop2
574+
; ALL-NEXT: f32.const $push1=, 0x1p-1
575+
; ALL-NEXT: f32.mul $push4=, $pop3, $pop1
576+
; ALL-NEXT: call $push5=, __truncsfhf2, $pop4
577+
; ALL-NEXT: return $pop5
604578
start:
605579
%y = fmul half %x, 0xH4000
606580
%z = fdiv half %y, 0xH4000
@@ -609,16 +583,15 @@ start:
609583

610584
define half @test_select_cc(half) nounwind {
611585
; ALL-LABEL: test_select_cc:
612-
; ALL: .functype test_select_cc (f32) -> (f32)
586+
; ALL: .functype test_select_cc (i32) -> (i32)
613587
; ALL-NEXT: # %bb.0:
614-
; ALL-NEXT: f32.const $push4=, 0x1p0
588+
; ALL-NEXT: i32.const $push4=, 15360
589+
; ALL-NEXT: i32.const $push3=, 0
590+
; ALL-NEXT: local.get $push6=, 0
591+
; ALL-NEXT: call $push1=, __extendhfsf2, $pop6
615592
; ALL-NEXT: f32.const $push0=, 0x0p0
616-
; ALL-NEXT: local.get $push7=, 0
617-
; ALL-NEXT: call $push1=, __truncsfhf2, $pop7
618-
; ALL-NEXT: call $push2=, __extendhfsf2, $pop1
619-
; ALL-NEXT: f32.const $push6=, 0x0p0
620-
; ALL-NEXT: f32.ne $push3=, $pop2, $pop6
621-
; ALL-NEXT: f32.select $push5=, $pop4, $pop0, $pop3
593+
; ALL-NEXT: f32.ne $push2=, $pop1, $pop0
594+
; ALL-NEXT: i32.select $push5=, $pop4, $pop3, $pop2
622595
; ALL-NEXT: return $pop5
623596
%2 = fcmp une half %0, 0xH0000
624597
%3 = uitofp i1 %2 to half
@@ -627,27 +600,28 @@ define half @test_select_cc(half) nounwind {
627600

628601
define half @fabs(half %x) nounwind {
629602
; ALL-LABEL: fabs:
630-
; ALL: .functype fabs (f32) -> (f32)
603+
; ALL: .functype fabs (i32) -> (i32)
631604
; ALL-NEXT: # %bb.0:
632-
; ALL-NEXT: local.get $push3=, 0
633-
; ALL-NEXT: call $push0=, __truncsfhf2, $pop3
634-
; ALL-NEXT: call $push1=, __extendhfsf2, $pop0
635-
; ALL-NEXT: f32.abs $push2=, $pop1
636-
; ALL-NEXT: return $pop2
605+
; ALL-NEXT: local.get $push2=, 0
606+
; ALL-NEXT: i32.const $push0=, 32767
607+
; ALL-NEXT: i32.and $push1=, $pop2, $pop0
608+
; ALL-NEXT: return $pop1
637609
%a = call half @llvm.fabs.f16(half %x)
638610
ret half %a
639611
}
640612

641613
define half @fcopysign(half %x, half %y) nounwind {
642614
; ALL-LABEL: fcopysign:
643-
; ALL: .functype fcopysign (f32, f32) -> (f32)
615+
; ALL: .functype fcopysign (i32, i32) -> (i32)
644616
; ALL-NEXT: # %bb.0:
645-
; ALL-NEXT: local.get $push3=, 0
646-
; ALL-NEXT: call $push0=, __truncsfhf2, $pop3
647-
; ALL-NEXT: call $push1=, __extendhfsf2, $pop0
648-
; ALL-NEXT: local.get $push4=, 1
649-
; ALL-NEXT: f32.copysign $push2=, $pop1, $pop4
650-
; ALL-NEXT: return $pop2
617+
; ALL-NEXT: local.get $push5=, 0
618+
; ALL-NEXT: i32.const $push2=, 32767
619+
; ALL-NEXT: i32.and $push3=, $pop5, $pop2
620+
; ALL-NEXT: local.get $push6=, 1
621+
; ALL-NEXT: i32.const $push0=, -32768
622+
; ALL-NEXT: i32.and $push1=, $pop6, $pop0
623+
; ALL-NEXT: i32.or $push4=, $pop3, $pop1
624+
; ALL-NEXT: return $pop4
651625
%a = call half @llvm.copysign.f16(half %x, half %y)
652626
ret half %a
653627
}

0 commit comments

Comments
 (0)