Skip to content

Commit

Permalink
Merge pull request #2813 from kinke/shippable
Browse files Browse the repository at this point in the history
AArch64: Fix C++ mangling of va_list & apply ltsmaster's IndirectByvalRewrite fix
  • Loading branch information
kinke authored Aug 17, 2018
2 parents ae712cf + b4a2a90 commit 9bd4fb2
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 94 deletions.
147 changes: 62 additions & 85 deletions gen/abi-aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,42 @@
#include "gen/abi-generic.h"
#include "gen/abi-aarch64.h"

/**
* The AACPS64 uses a special native va_list type:
*
* typedef struct __va_list {
* void *__stack; // next stack param
* void *__gr_top; // end of GP arg reg save area
* void *__vr_top; // end of FP/SIMD arg reg save area
* int __gr_offs; // offset from __gr_top to next GP register arg
* int __vr_offs; // offset from __vr_top to next FP/SIMD register arg
* } va_list;
*
* In druntime, the struct is defined as object.__va_list, an alias of
* ldc.internal.vararg.std.__va_list.
* Arguments of this type are never passed by value, only by reference (even
* though the mangled function name indicates otherwise!). This requires a
* little bit of compiler magic in the following implementations.
*/
struct AArch64TargetABI : TargetABI {
private:
IndirectByvalRewrite byvalRewrite;
HFAToArray hfaToArray;
CompositeToArray64 compositeToArray64;
IntegerRewrite integerRewrite;

bool isVaList(Type *t) {
return t->ty == Tstruct && strcmp(t->toPrettyChars(true),
"ldc.internal.vararg.std.__va_list") == 0;
}

bool passIndirectlyByValue(Type *t) {
t = t->toBasetype();
return t->ty == Tsarray || (t->ty == Tstruct && t->size() > 16 &&
!isHFA(static_cast<TypeStruct *>(t)));
}

public:
bool returnInArg(TypeFunction *tf, bool) override {
if (tf->isref) {
return false;
Expand All @@ -32,115 +63,61 @@ struct AArch64TargetABI : TargetABI {
if (!isPOD(rt))
return true;

return passByVal(tf, rt);
return passIndirectlyByValue(rt);
}

bool passByVal(TypeFunction *, Type *t) override {
t = t->toBasetype();
return t->ty == Tsarray ||
(t->ty == Tstruct && t->size() > 16 && !isHFA((TypeStruct *)t));
}
bool passByVal(TypeFunction *, Type *) override { return false; }

void rewriteFunctionType(IrFuncTy &fty) override {
Type *retTy = fty.ret->type->toBasetype();
if (!fty.ret->byref && retTy->ty == Tstruct) {
// Rewrite HFAs only because union HFAs are turned into IR types that are
// non-HFA and messes up register selection
if (isHFA((TypeStruct *)retTy, &fty.ret->ltype)) {
hfaToArray.applyTo(*fty.ret, fty.ret->ltype);
} else {
integerRewrite.applyTo(*fty.ret);
}
Type *rt = fty.ret->type->toBasetype();
if (!fty.ret->byref && rt->ty != Tvoid) {
rewriteArgument(fty, *fty.ret, true);
}

for (auto arg : fty.args) {
if (!arg->byref)
if (!arg->byref) {
rewriteArgument(fty, *arg);
}
}
}

void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
// FIXME
Type *ty = arg.type->toBasetype();

if (ty->ty == Tstruct || ty->ty == Tsarray) {
// Rewrite HFAs only because union HFAs are turned into IR types that are
// non-HFA and messes up register selection
if (ty->ty == Tstruct && isHFA((TypeStruct *)ty, &arg.ltype)) {
hfaToArray.applyTo(arg, arg.ltype);
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg, bool isReturnVal) {
const auto t = arg.type->toBasetype();

if (t->ty != Tstruct && t->ty != Tsarray)
return;

if (!isReturnVal && isVaList(t)) {
// compiler magic: pass va_list args implicitly by reference
arg.byref = true;
arg.ltype = arg.ltype->getPointerTo();
} else if (!isReturnVal && passIndirectlyByValue(t)) {
byvalRewrite.applyTo(arg);
}
// Rewrite HFAs only because union HFAs are turned into IR types that are
// non-HFA and messes up register selection
else if (t->ty == Tstruct &&
isHFA(static_cast<TypeStruct *>(t), &arg.ltype)) {
hfaToArray.applyTo(arg, arg.ltype);
} else {
if (isReturnVal) {
integerRewrite.applyTo(arg);
} else {
compositeToArray64.applyTo(arg);
}
}
}

/**
* The AACPS64 uses a special native va_list type:
*
* typedef struct __va_list {
* void *__stack; // next stack param
* void *__gr_top; // end of GP arg reg save area
* void *__vr_top; // end of FP/SIMD arg reg save area
* int __gr_offs; // offset from __gr_top to next GP register arg
* int __vr_offs; // offset from __vr_top to next FP/SIMD register arg
* } va_list;
*
* In druntime, the struct is defined as object.__va_list; the actually used
* core.stdc.stdarg.va_list type is a __va_list* pointer though to achieve
* byref semantics.
* This requires a little bit of compiler magic in the following
* implementations.
*/

LLType *getValistType() {
LLType *intType = LLType::getInt32Ty(gIR->context());
LLType *voidPointerType = getVoidPtrType();

std::vector<LLType *> parts; // struct __va_list {
parts.push_back(voidPointerType); // void *__stack;
parts.push_back(voidPointerType); // void *__gr_top;
parts.push_back(voidPointerType); // void *__vr_top;
parts.push_back(intType); // int __gr_offs;
parts.push_back(intType); // int __vr_offs; };

return LLStructType::get(gIR->context(), parts);
}

LLValue *prepareVaStart(DLValue *ap) override {
// Since the user only created a __va_list* pointer (ap) on the stack before
// invoking va_start, we first need to allocate the actual __va_list struct
// and set `ap` to its address.
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
DtoStore(valistmem,
DtoBitCast(DtoLVal(ap), getPtrToType(valistmem->getType())));
// Pass a i8* pointer to the actual struct to LLVM's va_start intrinsic.
return DtoBitCast(valistmem, getVoidPtrType());
}

void vaCopy(DLValue *dest, DValue *src) override {
// Analog to va_start, we first need to allocate a new __va_list struct on
// the stack and set `dest` to its address.
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
DtoStore(valistmem,
DtoBitCast(DtoLVal(dest), getPtrToType(valistmem->getType())));
// Then fill the new struct with a bitcopy of the source struct.
// `src` is a __va_list* pointer to the source struct.
DtoMemCpy(valistmem, DtoRVal(src));
}

LLValue *prepareVaArg(DLValue *ap) override {
// Pass a i8* pointer to the actual __va_list struct to LLVM's va_arg
// intrinsic.
return DtoBitCast(DtoRVal(ap), getVoidPtrType());
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
rewriteArgument(fty, arg, false);
}

Type *vaListType() override {
// We need to pass the actual va_list type for correct mangling. Simply
// using TypeIdentifier here is a bit wonky but works, as long as the name
// is actually available in the scope (this is what DMD does, so if a better
// solution is found there, this should be adapted).
return createTypeIdentifier(Loc(), Identifier::idPool("__va_list"))
->pointerTo();
return createTypeIdentifier(Loc(), Identifier::idPool("__va_list"));
}

const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
Expand Down
2 changes: 1 addition & 1 deletion runtime/druntime
48 changes: 40 additions & 8 deletions shippable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,61 @@ build:
- git checkout -b _backup
- git checkout ltsmaster
- git submodule update
- mkdir build-bootstrap
- cd build-bootstrap
- mkdir build-ltsmaster
- cd build-ltsmaster
- |
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ROOT_DIR=$PWD/../llvm \
-DCMAKE_INSTALL_PREFIX=$PWD/../bootstrap \
-DCMAKE_INSTALL_PREFIX=$PWD/../ldc-ltsmaster \
..
- ninja install
- cd ..
- bootstrap/bin/ldc2 --version
# Build actual version
- ldc-ltsmaster/bin/ldc2 --version
# Build actual version, for another bootstrapping step
- git checkout _backup
- git submodule update
- mkdir build-bootstrap
- cd build-bootstrap
- |
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DLLVM_ROOT_DIR=$PWD/../llvm \
-DCMAKE_INSTALL_PREFIX=$PWD/../ldc-bootstrap \
-DD_COMPILER=$PWD/../ldc-ltsmaster/bin/ldmd2 \
..
- ninja install
- cd ..
- ldc-bootstrap/bin/ldc2 --version
# Build with itself
- mkdir build
- cd build
- |
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DLLVM_ROOT_DIR=$PWD/../llvm \
-DLDC_INSTALL_LTOPLUGIN=ON \
-DLDC_INSTALL_LLVM_RUNTIME_LIBS=ON \
-DD_COMPILER=$PWD/../bootstrap/bin/ldmd2 \
-DD_COMPILER=$PWD/../ldc-bootstrap/bin/ldmd2 \
..
- ninja ldc2
- ninja
- bin/ldc2 --version
# Build druntime/Phobos unittest runners
- ninja -j16 all-test-runners
# Build and run LDC D unittests
- ctest --output-on-failure -R ldc2-unittest
# Run LIT testsuite
- ctest -V -R lit-tests || true
# Run DMD testsuite (non-debug only for now)
- DMD_TESTSUITE_MAKE_ARGS='-j16 -k' ctest -V -R dmd-testsuite -E "-debug$" || true
# Run druntime/Phobos unittests (non-debug only for now)
- ctest -j16 --output-on-failure -E "dmd-testsuite|ldc2-unittest|lit-tests|-debug(-shared)?$" || true

integrations:
notifications:
- integrationName: email
type: email
on_success: never
on_failure: never
on_cancel: never
on_pull_request: never

0 comments on commit 9bd4fb2

Please sign in to comment.