Skip to content
Open
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
9 changes: 5 additions & 4 deletions clang/lib/CodeGen/Targets/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,11 @@ RValue WebAssemblyABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
bool IsIndirect = isAggregateTypeForABI(Ty) &&
!isEmptyRecord(getContext(), Ty, true) &&
!isSingleElementStruct(Ty, getContext());
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect,
getContext().getTypeInfoInChars(Ty),
CharUnits::fromQuantity(4),
/*AllowHigherAlign=*/true, Slot);
return emitVoidPtrVAArg(
CGF, VAListAddr, Ty, IsIndirect, getContext().getTypeInfoInChars(Ty),
CharUnits::fromQuantity(
getContext().getTargetInfo().getTriple().isArch64Bit() ? 8 : 4),
/*AllowHigherAlign=*/true, Slot);
}

std::unique_ptr<TargetCodeGenInfo>
Expand Down
125 changes: 125 additions & 0 deletions clang/test/CodeGen/WebAssembly/wasm64-varargs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -o - -emit-llvm %s | FileCheck %s

#include <stdarg.h>

// CHECK-LABEL: define {{[^@]+}}@test_i32
// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[VA]])
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 8
// CHECK-NEXT: store i32 [[TMP0]], ptr [[V]], align 4
// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[VA]])
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[V]], align 4
// CHECK-NEXT: ret i32 [[TMP1]]
//
int test_i32(char *fmt, ...) {
va_list va;

va_start(va, fmt);
int v = va_arg(va, int);
va_end(va);

return v;
}


// CHECK-LABEL: define {{[^@]+}}@test_i64
// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[V:%.*]] = alloca i64, align 8
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[VA]])
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[ARGP_CUR]], align 8
// CHECK-NEXT: store i64 [[TMP1]], ptr [[V]], align 8
// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[VA]])
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[V]], align 8
// CHECK-NEXT: ret i64 [[TMP2]]
//
long long test_i64(char *fmt, ...) {
va_list va;

va_start(va, fmt);
long long v = va_arg(va, long long);
va_end(va);

return v;
}


struct S {
int x;
int y;
int z;
};

// CHECK-LABEL: define {{[^@]+}}@test_struct
// CHECK-SAME: (ptr dead_on_unwind noalias writable sret([[STRUCT_S:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[VA]])
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 8
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_RESULT]], ptr align 4 [[TMP0]], i64 12, i1 false)
// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[VA]])
// CHECK-NEXT: ret void
//
struct S test_struct(char *fmt, ...) {
va_list va;

va_start(va, fmt);
struct S v = va_arg(va, struct S);
va_end(va);

return v;
}


struct Z {};

// CHECK-LABEL: define {{[^@]+}}@test_empty_struct
// CHECK-SAME: (ptr dead_on_unwind noalias writable sret([[STRUCT_S:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[U:%.*]] = alloca [[STRUCT_Z:%.*]], align 1
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 8
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[VA]])
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 8
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 0
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[U]], ptr align 8 [[ARGP_CUR]], i64 0, i1 false)
// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[VA]], align 8
// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i64 8
// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[VA]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR1]], align 8
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_RESULT]], ptr align 4 [[TMP0]], i64 12, i1 false)
// CHECK-NEXT: call void @llvm.va_end.p0(ptr [[VA]])
// CHECK-NEXT: ret void
//
struct S test_empty_struct(char *fmt, ...) {
va_list va;

va_start(va, fmt);
struct Z u = va_arg(va, struct Z);
struct S v = va_arg(va, struct S);
va_end(va);

return v;
}
4 changes: 2 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1406,13 +1406,13 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
// Outgoing non-fixed arguments are placed in a buffer. First
// compute their offsets and the total amount of buffer space needed.
for (unsigned I = NumFixedArgs; I < Outs.size(); ++I) {
const ISD::OutputArg &Out = Outs[I];
SDValue &Arg = OutVals[I];
EVT VT = Arg.getValueType();
assert(VT != MVT::iPTR && "Legalized args should be concrete");
Type *Ty = VT.getTypeForEVT(*DAG.getContext());
Align Alignment =
std::max(Out.Flags.getNonZeroOrigAlign(), Layout.getABITypeAlign(Ty));
std::max(Align(Subtarget->getTargetTriple().isArch64Bit() ? 8 : 4),
Layout.getABITypeAlign(Ty));
unsigned Offset =
CCInfo.AllocateStack(Layout.getTypeAllocSize(Ty), Alignment);
CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(),
Expand Down