Skip to content

Commit

Permalink
Merge pull request #4652 from heiher/master
Browse files Browse the repository at this point in the history
Fix LoongArch support
  • Loading branch information
kinke committed May 23, 2024
2 parents dedd0c2 + 60a5517 commit c91e199
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 12 deletions.
132 changes: 124 additions & 8 deletions gen/abi/loongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//===----------------------------------------------------------------------===//
//
// ABI spec:
// https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html
// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc
//
//===----------------------------------------------------------------------===//

Expand All @@ -20,9 +20,101 @@
#include "gen/llvmhelpers.h"
#include "gen/tollvm.h"

using namespace dmd;

namespace {
struct Integer2Rewrite : BaseBitcastABIRewrite {
LLType *type(Type *t) override {
return LLStructType::get(gIR->context(),
{DtoType(Type::tint64), DtoType(Type::tint64)});
}
};

struct FlattenedFields {
Type *fields[2];
int length = 0; // use -1 to represent "no need to rewrite" condition
};

FlattenedFields visitStructFields(Type *ty, unsigned baseOffset) {
// recursively visit a POD struct to flatten it
FlattenedFields result;
if (auto ts = ty->isTypeStruct()) {
for (auto fi : ts->sym->fields) {
auto sub =
visitStructFields(fi->type->toBasetype(), baseOffset + fi->offset);
if (sub.length == -1 || result.length + sub.length > 2) {
result.length = -1;
return result;
}
for (unsigned i = 0; i < (unsigned)sub.length; ++i) {
result.fields[result.length++] = sub.fields[i];
}
}
return result;
}
switch (ty->ty) {
case TY::Tcomplex32: // treat it as {float32, float32}
result.fields[0] = pointerTo(Type::tfloat32);
result.fields[1] = pointerTo(Type::tfloat32);
result.length = 2;
break;
case TY::Tcomplex64: // treat it as {float64, float64}
result.fields[0] = pointerTo(Type::tfloat64);
result.fields[1] = pointerTo(Type::tfloat64);
result.length = 2;
break;
default:
if (ty->size() > 8) {
// field larger than GRLEN and FRLEN
result.length = -1;
break;
}
result.fields[0] = ty;
result.length = 1;
break;
}
return result;
}

struct HardfloatRewrite : BaseBitcastABIRewrite {
LLType *type(Type *ty, const FlattenedFields &flat) {
if (flat.length == 1) {
return LLStructType::get(gIR->context(), {DtoType(flat.fields[0])},
false);
}
assert(flat.length == 2);
LLType *t[2];
for (unsigned i = 0; i < 2; ++i) {
t[i] =
flat.fields[i]->isfloating()
? DtoType(flat.fields[i])
: LLIntegerType::get(gIR->context(), flat.fields[i]->size() * 8);
}
return LLStructType::get(gIR->context(), {t[0], t[1]}, false);
}
LLType *type(Type *ty) override {
return type(ty, visitStructFields(ty->toBasetype(), 0));
}
};
} // anonymous namespace

struct LoongArch64TargetABI : TargetABI {
private:
HardfloatRewrite hardfloatRewrite;
IndirectByvalRewrite indirectByvalRewrite{};
Integer2Rewrite integer2Rewrite;
IntegerRewrite integerRewrite;

bool requireHardfloatRewrite(Type *ty) {
if (!isPOD(ty) || !ty->toBasetype()->isTypeStruct())
return false;
auto result = visitStructFields(ty->toBasetype(), 0);
if (result.length <= 0)
return false;
if (result.length == 1)
return result.fields[0]->isfloating();
return result.fields[0]->isfloating() || result.fields[1]->isfloating();
}

public:
auto returnInArg(TypeFunction *tf, bool) -> bool override {
Expand All @@ -45,27 +137,51 @@ struct LoongArch64TargetABI : TargetABI {
}

void rewriteFunctionType(IrFuncTy &fty) override {
if (!fty.ret->byref) {
rewriteArgument(fty, *fty.ret);
if (!skipReturnValueRewrite(fty)) {
if (requireHardfloatRewrite(fty.ret->type)) {
// rewrite here because we should not apply this to variadic arguments
hardfloatRewrite.applyTo(*fty.ret);
} else {
rewriteArgument(fty, *fty.ret);
}
}

for (auto arg : fty.args) {
if (!arg->byref) {
rewriteArgument(fty, *arg);
if (requireHardfloatRewrite(arg->type)) {
// rewrite here because we should not apply this to variadic arguments
hardfloatRewrite.applyTo(*arg);
} else {
rewriteArgument(fty, *arg);
}
}
}
}

void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
if (arg.byref) {
return;
}

if (!isPOD(arg.type)) {
// non-PODs should be passed in memory
indirectByvalRewrite.applyTo(arg);
return;
}

Type *ty = arg.type->toBasetype();
if (ty->isintegral() && (ty->ty == TY::Tint32 || ty->ty == TY::Tuns32 ||
ty->ty == TY::Tdchar)) {
// In the LP64D ABI, both int32 and unsigned int32 are stored in
// general-purpose registers as proper sign extensions of their
// 32-bit values. So, the native ABI function's int32 arguments and
// return values should have the `signext` attribute.
// C example: https://godbolt.org/z/vcjErxj76
arg.attrs.addAttribute(LLAttribute::SExt);
} else if (isAggregate(ty) && ty->size() && ty->size() <= 16) {
if (ty->size() > 8 && DtoAlignment(ty) < 16) {
// pass the aggregate as {int64, int64} to avoid wrong alignment
integer2Rewrite.applyToIfNotObsolete(arg);
} else {
integerRewrite.applyToIfNotObsolete(arg);
}
}
}
};

Expand Down
7 changes: 4 additions & 3 deletions runtime/druntime/src/core/thread/fiber.d
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ private
extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc;
version (AArch64)
extern (C) void fiber_trampoline() nothrow;
version (LoongArch64)
extern (C) void fiber_trampoline() nothrow;
}
else version (LDC_Windows)
{
Expand Down Expand Up @@ -1831,7 +1833,6 @@ private:
// Like others, FP registers and return address ($r1) are kept
// below the saved stack top (tstack) to hide from GC scanning.
// fiber_switchContext expects newp sp to look like this:
// 10: $r21 (reserved)
// 9: $r22 (frame pointer)
// 8: $r23
// ...
Expand All @@ -1847,8 +1848,8 @@ private:

// Only need to set return address ($r1). Everything else is fine
// zero initialized.
pstack -= size_t.sizeof * 11; // skip past space reserved for $r21-$r31
push (cast(size_t) &fiber_entryPoint);
pstack -= size_t.sizeof * 10; // skip past space reserved for $r22-$r31
push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs
pstack += size_t.sizeof; // adjust sp (newp) above lr
}
else version (AsmAArch64_Posix)
Expand Down
25 changes: 24 additions & 1 deletion runtime/druntime/src/core/threadasm.S
Original file line number Diff line number Diff line change
Expand Up @@ -544,9 +544,10 @@ fiber_switchContext:
*/
.text
.globl fiber_switchContext
.type fiber_switchContext, %function
fiber_switchContext:
.cfi_startproc
.cfi_undefined $ra
.cfi_undefined 1
# reserve space on stack
addi.d $sp, $sp, -19 * 8

Expand Down Expand Up @@ -610,6 +611,28 @@ fiber_switchContext:

jr $ra
.cfi_endproc

/**
* When generating any kind of backtrace (gdb, exception handling) for
* a function called in a Fiber, we need to tell the unwinder to stop
* at our Fiber main entry point, i.e. we need to mark the bottom of
* the call stack. This can be done by clearing the return address register $ra
* prior to calling fiber_entryPoint (i.e. in fiber_switchContext) or
* using a .cfi_undefined directive for the return address register in the
* Fiber entry point. cfi_undefined seems to yield better results in gdb.
* Unfortunately we can't place it into fiber_entryPoint using inline
* asm, so we use this trampoline instead.
*/
.text
.global CSYM(fiber_trampoline)
.p2align 2
.type fiber_trampoline, %function
CSYM(fiber_trampoline):
.cfi_startproc
.cfi_undefined 1
// fiber_entryPoint never returns
bl CSYM(fiber_entryPoint)
.cfi_endproc
#elif defined(__arm__) && (defined(__ARM_EABI__) || defined(__APPLE__))
/************************************************************************************
* ARM ASM BITS
Expand Down

0 comments on commit c91e199

Please sign in to comment.