Skip to content

[SystemZ] Add support for half (fp16) #109164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,7 @@ to ``float``; see below for more information on this emulation.
* SPIR (natively)
* X86 (if SSE2 is available; natively if AVX512-FP16 is also available)
* RISC-V (natively if Zfh or Zhinx is available)
* SystemZ (emulated)

* ``__bf16`` is supported on the following targets (currently never natively):

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ class TargetInfo : public TransferrableTargetInfo,
bool NoAsmVariants; // True if {|} are normal characters.
bool HasLegalHalfType; // True if the backend supports operations on the half
// LLVM IR type.
bool HalfArgsAndReturns;
bool HalfArgsAndReturns; // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half) type.
bool HasFloat128;
bool HasFloat16;
bool HasBFloat16;
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Basic/Targets/SystemZ.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,24 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
"-v128:64-a:8:16-n32:64");
}
MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 128;

// True if the backend supports operations on the half LLVM IR type.
// By setting this to false, conversions will happen for _Float16 around
// a statement by default, with operations done in float. However, if
// -ffloat16-excess-precision=none is given, no conversions will be made
// and instead the backend will promote each half operation to float
// individually.
HasLegalHalfType = false;
// Support _Float16.
HasFloat16 = true;

HasStrictFP = true;
}

unsigned getMinGlobalAlign(uint64_t Size, bool HasNonWeakDef) const override;

bool useFP16ConversionIntrinsics() const override { return false; }

void getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const override;

Expand Down
40 changes: 26 additions & 14 deletions clang/lib/CodeGen/Targets/SystemZ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class SystemZABIInfo : public ABIInfo {
bool isPromotableIntegerTypeForABI(QualType Ty) const;
bool isCompoundType(QualType Ty) const;
bool isVectorArgumentType(QualType Ty) const;
bool isFPArgumentType(QualType Ty) const;
llvm::Type *getFPArgumentType(QualType Ty, uint64_t Size) const;
QualType GetSingleElementType(QualType Ty) const;

ABIArgInfo classifyReturnType(QualType RetTy) const;
Expand Down Expand Up @@ -107,7 +107,8 @@ class SystemZTargetCodeGenInfo : public TargetCodeGenInfo {
return nullptr;

llvm::Type *Ty = V->getType();
if (Ty->isFloatTy() || Ty->isDoubleTy() || Ty->isFP128Ty()) {
if (Ty->isHalfTy() || Ty->isFloatTy() || Ty->isDoubleTy() ||
Ty->isFP128Ty()) {
llvm::Module &M = CGM.getModule();
auto &Ctx = M.getContext();
llvm::Function *TDCFunc = llvm::Intrinsic::getOrInsertDeclaration(
Expand Down Expand Up @@ -179,20 +180,31 @@ bool SystemZABIInfo::isVectorArgumentType(QualType Ty) const {
getContext().getTypeSize(Ty) <= 128);
}

bool SystemZABIInfo::isFPArgumentType(QualType Ty) const {
// The Size argument will in case of af an overaligned single element struct
// reflect the overalignment value. In such a case the argument will be
// passed using the type matching Size.
llvm::Type *SystemZABIInfo::getFPArgumentType(QualType Ty,
uint64_t Size) const {
if (IsSoftFloatABI)
return false;
return nullptr;

if (const BuiltinType *BT = Ty->getAs<BuiltinType>())
switch (BT->getKind()) {
case BuiltinType::Float16:
if (Size == 16)
return llvm::Type::getHalfTy(getVMContext());
LLVM_FALLTHROUGH;
case BuiltinType::Float:
if (Size == 32)
return llvm::Type::getFloatTy(getVMContext());
LLVM_FALLTHROUGH;
case BuiltinType::Double:
return true;
return llvm::Type::getDoubleTy(getVMContext());
default:
return false;
return nullptr;
}

return false;
return nullptr;
}

QualType SystemZABIInfo::GetSingleElementType(QualType Ty) const {
Expand Down Expand Up @@ -277,7 +289,8 @@ RValue SystemZABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
} else {
if (AI.getCoerceToType())
ArgTy = AI.getCoerceToType();
InFPRs = (!IsSoftFloatABI && (ArgTy->isFloatTy() || ArgTy->isDoubleTy()));
InFPRs = (!IsSoftFloatABI &&
(ArgTy->isHalfTy() || ArgTy->isFloatTy() || ArgTy->isDoubleTy()));
IsVector = ArgTy->isVectorTy();
UnpaddedSize = TyInfo.Width;
DirectAlign = TyInfo.Align;
Expand Down Expand Up @@ -447,12 +460,11 @@ ABIArgInfo SystemZABIInfo::classifyArgumentType(QualType Ty) const {
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
/*ByVal=*/false);

// The structure is passed as an unextended integer, a float, or a double.
if (isFPArgumentType(SingleElementTy)) {
assert(Size == 32 || Size == 64);
return ABIArgInfo::getDirect(
Size == 32 ? llvm::Type::getFloatTy(getVMContext())
: llvm::Type::getDoubleTy(getVMContext()));
// The structure is passed as an unextended integer, a half, a float,
// or a double.
if (llvm::Type *FPArgTy = getFPArgumentType(SingleElementTy, Size)) {
assert(Size == 16 || Size == 32 || Size == 64);
return ABIArgInfo::getDirect(FPArgTy);
} else {
llvm::IntegerType *PassTy = llvm::IntegerType::get(getVMContext(), Size);
return Size <= 32 ? ABIArgInfo::getNoExtend(PassTy)
Expand Down
85 changes: 85 additions & 0 deletions clang/test/CodeGen/SystemZ/Float16.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// RUN: %clang_cc1 -triple s390x-linux-gnu \
// RUN: -ffloat16-excess-precision=standard -emit-llvm -o - %s \
// RUN: | FileCheck %s -check-prefix=STANDARD

// RUN: %clang_cc1 -triple s390x-linux-gnu \
// RUN: -ffloat16-excess-precision=none -emit-llvm -o - %s \
// RUN: | FileCheck %s -check-prefix=NONE

// RUN: %clang_cc1 -triple s390x-linux-gnu \
// RUN: -ffloat16-excess-precision=fast -emit-llvm -o - %s \
// RUN: | FileCheck %s -check-prefix=FAST

_Float16 f(_Float16 a, _Float16 b, _Float16 c, _Float16 d) {
return a * b + c * d;
}

// STANDARD-LABEL: define dso_local half @f(half noundef %a, half noundef %b, half noundef %c, half noundef %d) #0 {
// STANDARD-NEXT: entry:
// STANDARD-NEXT: %a.addr = alloca half, align 2
// STANDARD-NEXT: %b.addr = alloca half, align 2
// STANDARD-NEXT: %c.addr = alloca half, align 2
// STANDARD-NEXT: %d.addr = alloca half, align 2
// STANDARD-NEXT: store half %a, ptr %a.addr, align 2
// STANDARD-NEXT: store half %b, ptr %b.addr, align 2
// STANDARD-NEXT: store half %c, ptr %c.addr, align 2
// STANDARD-NEXT: store half %d, ptr %d.addr, align 2
// STANDARD-NEXT: %0 = load half, ptr %a.addr, align 2
// STANDARD-NEXT: %ext = fpext half %0 to float
// STANDARD-NEXT: %1 = load half, ptr %b.addr, align 2
// STANDARD-NEXT: %ext1 = fpext half %1 to float
// STANDARD-NEXT: %mul = fmul float %ext, %ext1
// STANDARD-NEXT: %2 = load half, ptr %c.addr, align 2
// STANDARD-NEXT: %ext2 = fpext half %2 to float
// STANDARD-NEXT: %3 = load half, ptr %d.addr, align 2
// STANDARD-NEXT: %ext3 = fpext half %3 to float
// STANDARD-NEXT: %mul4 = fmul float %ext2, %ext3
// STANDARD-NEXT: %add = fadd float %mul, %mul4
// STANDARD-NEXT: %unpromotion = fptrunc float %add to half
// STANDARD-NEXT: ret half %unpromotion
// STANDARD-NEXT: }

// NONE-LABEL: define dso_local half @f(half noundef %a, half noundef %b, half noundef %c, half noundef %d) #0 {
// NONE-NEXT: entry:
// NONE-NEXT: %a.addr = alloca half, align 2
// NONE-NEXT: %b.addr = alloca half, align 2
// NONE-NEXT: %c.addr = alloca half, align 2
// NONE-NEXT: %d.addr = alloca half, align 2
// NONE-NEXT: store half %a, ptr %a.addr, align 2
// NONE-NEXT: store half %b, ptr %b.addr, align 2
// NONE-NEXT: store half %c, ptr %c.addr, align 2
// NONE-NEXT: store half %d, ptr %d.addr, align 2
// NONE-NEXT: %0 = load half, ptr %a.addr, align 2
// NONE-NEXT: %1 = load half, ptr %b.addr, align 2
// NONE-NEXT: %mul = fmul half %0, %1
// NONE-NEXT: %2 = load half, ptr %c.addr, align 2
// NONE-NEXT: %3 = load half, ptr %d.addr, align 2
// NONE-NEXT: %mul1 = fmul half %2, %3
// NONE-NEXT: %add = fadd half %mul, %mul1
// NONE-NEXT: ret half %add
// NONE-NEXT: }

// FAST-LABEL: define dso_local half @f(half noundef %a, half noundef %b, half noundef %c, half noundef %d) #0 {
// FAST-NEXT: entry:
// FAST-NEXT: %a.addr = alloca half, align 2
// FAST-NEXT: %b.addr = alloca half, align 2
// FAST-NEXT: %c.addr = alloca half, align 2
// FAST-NEXT: %d.addr = alloca half, align 2
// FAST-NEXT: store half %a, ptr %a.addr, align 2
// FAST-NEXT: store half %b, ptr %b.addr, align 2
// FAST-NEXT: store half %c, ptr %c.addr, align 2
// FAST-NEXT: store half %d, ptr %d.addr, align 2
// FAST-NEXT: %0 = load half, ptr %a.addr, align 2
// FAST-NEXT: %ext = fpext half %0 to float
// FAST-NEXT: %1 = load half, ptr %b.addr, align 2
// FAST-NEXT: %ext1 = fpext half %1 to float
// FAST-NEXT: %mul = fmul float %ext, %ext1
// FAST-NEXT: %2 = load half, ptr %c.addr, align 2
// FAST-NEXT: %ext2 = fpext half %2 to float
// FAST-NEXT: %3 = load half, ptr %d.addr, align 2
// FAST-NEXT: %ext3 = fpext half %3 to float
// FAST-NEXT: %mul4 = fmul float %ext2, %ext3
// FAST-NEXT: %add = fadd float %mul, %mul4
// FAST-NEXT: %unpromotion = fptrunc float %add to half
// FAST-NEXT: ret half %unpromotion
// FAST-NEXT: }
39 changes: 39 additions & 0 deletions clang/test/CodeGen/SystemZ/fp16.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %clang_cc1 -triple s390x-linux-gnu -emit-llvm -o - %s \
// RUN: | FileCheck %s

void f(__fp16 *a, __fp16 *b, __fp16 *c, __fp16 *d, __fp16 *e) {
*e = (*a) * (*b) + (*c) * (*d);
}

// CHECK-LABEL: define dso_local void @f(ptr noundef %a, ptr noundef %b, ptr noundef %c, ptr noundef %d, ptr noundef %e) #0 {
// CHECK-NEXT: entry:
// CHECK-NEXT: %a.addr = alloca ptr, align 8
// CHECK-NEXT: %b.addr = alloca ptr, align 8
// CHECK-NEXT: %c.addr = alloca ptr, align 8
// CHECK-NEXT: %d.addr = alloca ptr, align 8
// CHECK-NEXT: %e.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %a, ptr %a.addr, align 8
// CHECK-NEXT: store ptr %b, ptr %b.addr, align 8
// CHECK-NEXT: store ptr %c, ptr %c.addr, align 8
// CHECK-NEXT: store ptr %d, ptr %d.addr, align 8
// CHECK-NEXT: store ptr %e, ptr %e.addr, align 8
// CHECK-NEXT: %0 = load ptr, ptr %a.addr, align 8
// CHECK-NEXT: %1 = load half, ptr %0, align 2
// CHECK-NEXT: %conv = fpext half %1 to float
// CHECK-NEXT: %2 = load ptr, ptr %b.addr, align 8
// CHECK-NEXT: %3 = load half, ptr %2, align 2
// CHECK-NEXT: %conv1 = fpext half %3 to float
// CHECK-NEXT: %mul = fmul float %conv, %conv1
// CHECK-NEXT: %4 = load ptr, ptr %c.addr, align 8
// CHECK-NEXT: %5 = load half, ptr %4, align 2
// CHECK-NEXT: %conv2 = fpext half %5 to float
// CHECK-NEXT: %6 = load ptr, ptr %d.addr, align 8
// CHECK-NEXT: %7 = load half, ptr %6, align 2
// CHECK-NEXT: %conv3 = fpext half %7 to float
// CHECK-NEXT: %mul4 = fmul float %conv2, %conv3
// CHECK-NEXT: %add = fadd float %mul, %mul4
// CHECK-NEXT: %8 = fptrunc float %add to half
// CHECK-NEXT: %9 = load ptr, ptr %e.addr, align 8
// CHECK-NEXT: store half %8, ptr %9, align 2
// CHECK-NEXT: ret void
// CHECK-NEXT: }
14 changes: 13 additions & 1 deletion clang/test/CodeGen/SystemZ/strictfp_builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,24 @@

#pragma float_control(except, on)

// CHECK-LABEL: @test_isnan__Float16(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[F_ADDR:%.*]] = alloca half, align 2
// CHECK-NEXT: store half [[F:%.*]], ptr [[F_ADDR]], align 2
// CHECK-NEXT: [[TMP0:%.*]] = load half, ptr [[F_ADDR]], align 2
// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.s390.tdc.f16(half [[TMP0]], i64 15) #[[ATTR2:[0-9]+]]
// CHECK-NEXT: ret i32 [[TMP1]]
//
int test_isnan__Float16(_Float16 f) {
return __builtin_isnan(f);
}

// CHECK-LABEL: @test_isnan_float(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[F_ADDR:%.*]] = alloca float, align 4
// CHECK-NEXT: store float [[F:%.*]], ptr [[F_ADDR]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load float, ptr [[F_ADDR]], align 4
// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.s390.tdc.f32(float [[TMP0]], i64 15) #[[ATTR2:[0-9]+]]
// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.s390.tdc.f32(float [[TMP0]], i64 15) #[[ATTR2]]
// CHECK-NEXT: ret i32 [[TMP1]]
//
int test_isnan_float(float f) {
Expand Down
53 changes: 53 additions & 0 deletions clang/test/CodeGen/SystemZ/systemz-abi.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ long long pass_longlong(long long arg) { return arg; }
__int128 pass_int128(__int128 arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_int128(ptr dead_on_unwind noalias writable sret(i128) align 8 %{{.*}}, ptr %0)

_Float16 pass__Float16(_Float16 arg) { return arg; }
// CHECK-LABEL: define{{.*}} half @pass__Float16(half %{{.*}})

float pass_float(float arg) { return arg; }
// CHECK-LABEL: define{{.*}} float @pass_float(float %{{.*}})

Expand Down Expand Up @@ -79,6 +82,9 @@ _Complex long pass_complex_long(_Complex long arg) { return arg; }
_Complex long long pass_complex_longlong(_Complex long long arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_complex_longlong(ptr dead_on_unwind noalias writable sret({ i64, i64 }) align 8 %{{.*}}, ptr %{{.*}}arg)

_Complex _Float16 pass_complex__Float16(_Complex _Float16 arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_complex__Float16(ptr dead_on_unwind noalias writable sret({ half, half }) align 2 %{{.*}}, ptr %{{.*}}arg)

_Complex float pass_complex_float(_Complex float arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_complex_float(ptr dead_on_unwind noalias writable sret({ float, float }) align 4 %{{.*}}, ptr %{{.*}}arg)

Expand Down Expand Up @@ -130,6 +136,11 @@ struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; }

// Float-like aggregate types

struct agg__Float16 { _Float16 a; };
struct agg__Float16 pass_agg__Float16(struct agg__Float16 arg) { return arg; }
// HARD-FLOAT-LABEL: define{{.*}} void @pass_agg__Float16(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16) align 2 %{{.*}}, half %{{.*}})
// SOFT-FLOAT-LABEL: define{{.*}} void @pass_agg__Float16(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16) align 2 %{{.*}}, i16 noext %{{.*}})

struct agg_float { float a; };
struct agg_float pass_agg_float(struct agg_float arg) { return arg; }
// HARD-FLOAT-LABEL: define{{.*}} void @pass_agg_float(ptr dead_on_unwind noalias writable sret(%struct.agg_float) align 4 %{{.*}}, float %{{.*}})
Expand All @@ -144,6 +155,20 @@ struct agg_longdouble { long double a; };
struct agg_longdouble pass_agg_longdouble(struct agg_longdouble arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_agg_longdouble(ptr dead_on_unwind noalias writable sret(%struct.agg_longdouble) align 8 %{{.*}}, ptr %{{.*}})

struct agg__Float16_a4 { _Float16 a __attribute__((aligned (4))); };
struct agg__Float16_a4 pass_agg__Float16_a4(struct agg__Float16_a4 arg) { return arg; }
// HARD-FLOAT-LABEL: define{{.*}} void @pass_agg__Float16_a4(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16_a4) align 4 %{{.*}}, float %{{.*}})
// SOFT-FLOAT-LABEL: define{{.*}} void @pass_agg__Float16_a4(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16_a4) align 4 %{{.*}}, i32 noext %{{.*}})

struct agg__Float16_a8 { _Float16 a __attribute__((aligned (8))); };
struct agg__Float16_a8 pass_agg__Float16_a8(struct agg__Float16_a8 arg) { return arg; }
// HARD-FLOAT-LABEL: define{{.*}} void @pass_agg__Float16_a8(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16_a8) align 8 %{{.*}}, double %{{.*}})
// SOFT-FLOAT-LABEL: define{{.*}} void @pass_agg__Float16_a8(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16_a8) align 8 %{{.*}}, i64 %{{.*}})

struct agg__Float16_a16 { _Float16 a __attribute__((aligned (16))); };
struct agg__Float16_a16 pass_agg__Float16_a16(struct agg__Float16_a16 arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_agg__Float16_a16(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16_a16) align 16 %{{.*}}, ptr %{{.*}})

struct agg_float_a8 { float a __attribute__((aligned (8))); };
struct agg_float_a8 pass_agg_float_a8(struct agg_float_a8 arg) { return arg; }
// HARD-FLOAT-LABEL: define{{.*}} void @pass_agg_float_a8(ptr dead_on_unwind noalias writable sret(%struct.agg_float_a8) align 8 %{{.*}}, double %{{.*}})
Expand Down Expand Up @@ -171,6 +196,10 @@ struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; }

// Union types likewise are *not* float-like aggregate types

union union__Float16 { _Float16 a; };
union union__Float16 pass_union__Float16(union union__Float16 arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_union__Float16(ptr dead_on_unwind noalias writable sret(%union.union__Float16) align 2 %{{.*}}, i16 noext %{{.*}})

union union_float { float a; };
union union_float pass_union_float(union union_float arg) { return arg; }
// CHECK-LABEL: define{{.*}} void @pass_union_float(ptr dead_on_unwind noalias writable sret(%union.union_float) align 4 %{{.*}}, i32 noext %{{.*}})
Expand Down Expand Up @@ -448,6 +477,30 @@ struct agg_8byte va_agg_8byte(__builtin_va_list l) { return __builtin_va_arg(l,
// CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi ptr [ [[RAW_REG_ADDR]], %{{.*}} ], [ [[RAW_MEM_ADDR]], %{{.*}} ]
// CHECK: ret void

struct agg__Float16 va_agg__Float16(__builtin_va_list l) { return __builtin_va_arg(l, struct agg__Float16); }
// CHECK-LABEL: define{{.*}} void @va_agg__Float16(ptr dead_on_unwind noalias writable sret(%struct.agg__Float16) align 2 %{{.*}}, ptr %{{.*}}
// HARD-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds nuw %struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 1
// SOFT-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds nuw %struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 0
// CHECK: [[REG_COUNT:%[^ ]+]] = load i64, ptr [[REG_COUNT_PTR]]
// HARD-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4
// SOFT-FLOAT: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5
// CHECK: br i1 [[FITS_IN_REGS]],
// CHECK: [[SCALED_REG_COUNT:%[^ ]+]] = mul i64 [[REG_COUNT]], 8
// HARD-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 128
// SOFT-FLOAT: [[REG_OFFSET:%[^ ]+]] = add i64 [[SCALED_REG_COUNT]], 22
// CHECK: [[REG_SAVE_AREA_PTR:%[^ ]+]] = getelementptr inbounds nuw %struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 3
// CHECK: [[REG_SAVE_AREA:%[^ ]+]] = load ptr, ptr [[REG_SAVE_AREA_PTR:[^ ]+]]
// CHECK: [[RAW_REG_ADDR:%[^ ]+]] = getelementptr i8, ptr [[REG_SAVE_AREA]], i64 [[REG_OFFSET]]
// CHECK: [[REG_COUNT1:%[^ ]+]] = add i64 [[REG_COUNT]], 1
// CHECK: store i64 [[REG_COUNT1]], ptr [[REG_COUNT_PTR]]
// CHECK: [[OVERFLOW_ARG_AREA_PTR:%[^ ]+]] = getelementptr inbounds nuw %struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 2
// CHECK: [[OVERFLOW_ARG_AREA:%[^ ]+]] = load ptr, ptr [[OVERFLOW_ARG_AREA_PTR]]
// CHECK: [[RAW_MEM_ADDR:%[^ ]+]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i64 6
// CHECK: [[OVERFLOW_ARG_AREA2:%[^ ]+]] = getelementptr i8, ptr [[OVERFLOW_ARG_AREA]], i64 8
// CHECK: store ptr [[OVERFLOW_ARG_AREA2]], ptr [[OVERFLOW_ARG_AREA_PTR]]
// CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi ptr [ [[RAW_REG_ADDR]], %{{.*}} ], [ [[RAW_MEM_ADDR]], %{{.*}} ]
// CHECK: ret void

struct agg_float va_agg_float(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_float); }
// CHECK-LABEL: define{{.*}} void @va_agg_float(ptr dead_on_unwind noalias writable sret(%struct.agg_float) align 4 %{{.*}}, ptr %{{.*}}
// HARD-FLOAT: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds nuw %struct.__va_list_tag, ptr %{{.*}}, i32 0, i32 1
Expand Down
8 changes: 8 additions & 0 deletions clang/test/CodeGen/SystemZ/systemz-inline-asm.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ void test_M(void) {
// CHECK: call void asm sideeffect "#FOO $0", "M"(i32 2147483647)
}

_Float16 test_f16(_Float16 a) {
_Float16 f;
asm("ler %0, %1" : "=f" (f) : "f" (a));
return f;
// CHECK-LABEL: define{{.*}} half @test_f16(half noundef %a)
// CHECK: call half asm "ler $0, $1", "=f,f"(half %a)
}

float test_f32(float f, float g) {
asm("aebr %0, %2" : "=f" (f) : "0" (f), "f" (g));
return f;
Expand Down
Loading
Loading