[llvm] [AVR] Add a test for half support (NFC) (PR #152708)
Ben Shi via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 9 02:16:30 PDT 2025
================
@@ -0,0 +1,750 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc %s -o - -mtriple=avr | FileCheck %s
+
+; Tests for various operations on half precison float. Much of the test is
+; copied from test/CodeGen/X86/half.ll.
+
+define void @store(half %x, ptr %p) nounwind {
+; CHECK-LABEL: store:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r20
+; CHECK-NEXT: mov r31, r21
+; CHECK-NEXT: std Z+1, r23
+; CHECK-NEXT: st Z, r22
+; CHECK-NEXT: ret
+ store half %x, ptr %p
+ ret void
+}
+
+define half @return(ptr %p) nounwind {
+; CHECK-LABEL: return:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ld r22, Z
+; CHECK-NEXT: ldd r23, Z+1
+; CHECK-NEXT: ret
+ %r = load half, ptr %p
+ ret half %r
+}
+
+define dso_local double @loadd(ptr nocapture readonly %a) local_unnamed_addr nounwind {
+; CHECK-LABEL: loadd:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ldd r24, Z+2
+; CHECK-NEXT: ldd r25, Z+3
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __extendsfdf2
+; CHECK-NEXT: ret
+entry:
+ %arrayidx = getelementptr inbounds i16, ptr %a, i64 1
+ %0 = load i16, ptr %arrayidx, align 2
+ %1 = tail call double @llvm.convert.from.fp16.f64(i16 %0)
+ ret double %1
+}
+
+define dso_local float @loadf(ptr nocapture readonly %a) local_unnamed_addr nounwind {
+; CHECK-LABEL: loadf:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ldd r24, Z+2
+; CHECK-NEXT: ldd r25, Z+3
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: ret
+entry:
+ %arrayidx = getelementptr inbounds i16, ptr %a, i64 1
+ %0 = load i16, ptr %arrayidx, align 2
+ %1 = tail call float @llvm.convert.from.fp16.f32(i16 %0)
+ ret float %1
+}
+
+define dso_local void @stored(ptr nocapture %a, double %b) local_unnamed_addr nounwind {
+; CHECK-LABEL: stored:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: mov r30, r22
+; CHECK-NEXT: mov r31, r23
+; CHECK-NEXT: mov r22, r20
+; CHECK-NEXT: mov r23, r21
+; CHECK-NEXT: mov r20, r18
+; CHECK-NEXT: mov r21, r19
+; CHECK-NEXT: mov r18, r16
+; CHECK-NEXT: mov r19, r17
+; CHECK-NEXT: mov r16, r24
+; CHECK-NEXT: mov r17, r25
+; CHECK-NEXT: mov r24, r30
+; CHECK-NEXT: mov r25, r31
+; CHECK-NEXT: rcall __truncdfhf2
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: mov r30, r16
+; CHECK-NEXT: mov r31, r17
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call i16 @llvm.convert.to.fp16.f64(double %b)
+ store i16 %0, ptr %a, align 2
+ ret void
+}
+
+define dso_local void @storef(ptr nocapture %a, float %b) local_unnamed_addr nounwind {
+; CHECK-LABEL: storef:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: mov r18, r22
+; CHECK-NEXT: mov r19, r23
+; CHECK-NEXT: mov r16, r24
+; CHECK-NEXT: mov r17, r25
+; CHECK-NEXT: mov r22, r20
+; CHECK-NEXT: mov r23, r21
+; CHECK-NEXT: mov r24, r18
+; CHECK-NEXT: mov r25, r19
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: mov r30, r16
+; CHECK-NEXT: mov r31, r17
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call i16 @llvm.convert.to.fp16.f32(float %b)
+ store i16 %0, ptr %a, align 2
+ ret void
+}
+
+define void @test_load_store(ptr %in, ptr %out) nounwind {
+; CHECK-LABEL: test_load_store:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: mov r16, r22
+; CHECK-NEXT: mov r17, r23
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: mov r30, r16
+; CHECK-NEXT: mov r31, r17
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: ret
+ %val = load half, ptr %in
+ store half %val, ptr %out
+ ret void
+}
+
+define i16 @test_bitcast_from_half(ptr %addr) nounwind {
+; CHECK-LABEL: test_bitcast_from_half:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+; CHECK-NEXT: ret
+ %val = load half, ptr %addr
+ %val_int = bitcast half %val to i16
+ ret i16 %val_int
+}
+
+define void @test_bitcast_to_half(ptr %addr, i16 %in) nounwind {
+; CHECK-LABEL: test_bitcast_to_half:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: std Z+1, r23
+; CHECK-NEXT: st Z, r22
+; CHECK-NEXT: ret
+ %val_fp = bitcast i16 %in to half
+ store half %val_fp, ptr %addr
+ ret void
+}
+
+define half @from_bits(i16 %x) nounwind {
+; CHECK-LABEL: from_bits:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r22, r24
+; CHECK-NEXT: mov r23, r25
+; CHECK-NEXT: ret
+ %res = bitcast i16 %x to half
+ ret half %res
+}
+
+define i16 @to_bits(half %x) nounwind {
+; CHECK-LABEL: to_bits:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r24, r22
+; CHECK-NEXT: mov r25, r23
+; CHECK-NEXT: ret
+ %res = bitcast half %x to i16
+ ret i16 %res
+}
+
+define float @test_extend32(ptr %addr) nounwind {
+; CHECK-LABEL: test_extend32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: ret
+ %val16 = load half, ptr %addr
+ %val32 = fpext half %val16 to float
+ ret float %val32
+}
+
+define double @test_extend64(ptr %addr) nounwind {
+; CHECK-LABEL: test_extend64:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __extendsfdf2
+; CHECK-NEXT: ret
+ %val16 = load half, ptr %addr
+ %val32 = fpext half %val16 to double
+ ret double %val32
+}
+
+define void @test_trunc32(float %in, ptr %addr) nounwind {
+; CHECK-LABEL: test_trunc32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: mov r16, r20
+; CHECK-NEXT: mov r17, r21
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: mov r30, r16
+; CHECK-NEXT: mov r31, r17
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: ret
+ %val16 = fptrunc float %in to half
+ store half %val16, ptr %addr
+ ret void
+}
+
+define void @test_trunc64(double %in, ptr %addr) nounwind {
+; CHECK-LABEL: test_trunc64:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: rcall __truncdfhf2
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: mov r30, r16
+; CHECK-NEXT: mov r31, r17
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: ret
+ %val16 = fptrunc double %in to half
+ store half %val16, ptr %addr
+ ret void
+}
+
+define i64 @test_fptosi_i64(ptr %p) nounwind {
+; CHECK-LABEL: test_fptosi_i64:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __fixsfdi
+; CHECK-NEXT: ret
+ %a = load half, ptr %p, align 2
+ %r = fptosi half %a to i64
+ ret i64 %r
+}
+
+define void @test_sitofp_i64(i64 %a, ptr %p) nounwind {
+; CHECK-LABEL: test_sitofp_i64:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: rcall __floatdisf
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: mov r30, r16
+; CHECK-NEXT: mov r31, r17
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: ret
+ %r = sitofp i64 %a to half
+ store half %r, ptr %p
+ ret void
+}
+
+define i64 @test_fptoui_i64(ptr %p) nounwind {
+; CHECK-LABEL: test_fptoui_i64:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r30, r24
+; CHECK-NEXT: mov r31, r25
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __fixunssfdi
+; CHECK-NEXT: ret
+ %a = load half, ptr %p, align 2
+ %r = fptoui half %a to i64
+ ret i64 %r
+}
+
+define void @test_uitofp_i64(i64 %a, ptr %p) nounwind {
+; CHECK-LABEL: test_uitofp_i64:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: rcall __floatundisf
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: rcall __extendhfsf2
+; CHECK-NEXT: rcall __truncsfhf2
+; CHECK-NEXT: mov r30, r16
+; CHECK-NEXT: mov r31, r17
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: ret
+ %r = uitofp i64 %a to half
+ store half %r, ptr %p
+ ret void
+}
+
+define <4 x float> @test_extend32_vec4(ptr %p) nounwind {
----------------
benshi001 wrote:
AVR is a quite simple 8-bit CPU, it lacks HW float unit and vector unit. In real production environment, clang-AVR relies on libgcc-7.3, which also lacks infrastrure of float16. In fact avr-gcc 7.3 even attributes double to float, as indicated in https://gcc.gnu.org/wiki/avr-gcc.
So I think the best way is also attributing half type to float32, to fit avr-gcc-7.3 (and its libgcc) which is the main stream version in real production environment.
For vector types, the clang front end does not supprt vector type syntax, also other language fronts should not do that either.
https://github.com/llvm/llvm-project/pull/152708
More information about the llvm-commits
mailing list