mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 08:54:25 +00:00
ARM64 and macOS: AAPCS64 struct passing, aarch64/macos targets, Apple frameworks and ld messages, tests/cross.sh, CI on ARM64 Linux and macOS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
013b95d584
commit
484c14fe77
12 changed files with 256 additions and 78 deletions
54
src/abi.cpp
54
src/abi.cpp
|
|
@ -1,6 +1,7 @@
|
|||
// The C calling convention for structs passed and returned by value (System V x86-64),
|
||||
// the way clang does it: small structs travel in registers, split into eightbytes that are
|
||||
// either INTEGER (general registers) or SSE (xmm registers); bigger ones go through memory.
|
||||
// The C calling convention for structs passed and returned by value, the way clang does it:
|
||||
// System V x86-64 (Linux, macOS): small structs split into eightbytes, INTEGER or SSE registers
|
||||
// Microsoft x64 (Windows): 1/2/4/8 bytes in one register, everything else by address
|
||||
// AAPCS64 (ARM64 Linux, Apple): float groups in vector registers, else up to 16 bytes in x registers
|
||||
|
||||
#include "codegen_impl.h"
|
||||
#include "target.h"
|
||||
|
|
@ -45,6 +46,39 @@ AbiArg Codegen::classify(const FType &t) {
|
|||
AbiArg a;
|
||||
if (t.kind != FType::Struct) return a; // Direct
|
||||
uint64_t size = dl->getTypeAllocSize(ty(t));
|
||||
if (g_target.triple.isAArch64()) {
|
||||
// A homogeneous float aggregate (1-4 floats or 1-4 doubles) travels in vector registers.
|
||||
std::vector<Leaf> ls;
|
||||
leaves(*this, t, 0, ls);
|
||||
bool hfa = !ls.empty() && ls.size() <= 4;
|
||||
for (const Leaf &l : ls) hfa &= l.isFloat && l.isDouble == ls[0].isDouble;
|
||||
if (hfa) {
|
||||
llvm::Type *ft = ls[0].isDouble ? (llvm::Type *)b.getDoubleTy() : b.getFloatTy();
|
||||
a.kind = AbiArg::Expand;
|
||||
a.parts.push_back(ArrayType::get(ft, ls.size()));
|
||||
a.coerced = StructType::get(ctx, std::vector<llvm::Type *>(ls.size(), ft));
|
||||
a.stackAlign8 = !g_target.triple.isOSDarwin();
|
||||
return a;
|
||||
}
|
||||
if (size > 16) {
|
||||
a.kind = AbiArg::Indirect; // a copy, passed by address; returned through sret
|
||||
return a;
|
||||
}
|
||||
// up to 16 bytes in general registers: i64 or [2 x i64]; returned as an exact-size integer
|
||||
uint64_t align = dl->getABITypeAlign(ty(t)).value();
|
||||
a.kind = AbiArg::Expand;
|
||||
if (align >= 16) {
|
||||
a.parts.push_back(b.getInt128Ty());
|
||||
a.coerced = b.getInt128Ty();
|
||||
} else if (size <= 8) {
|
||||
a.parts.push_back(b.getInt64Ty());
|
||||
a.coerced = b.getIntNTy(size * 8);
|
||||
} else {
|
||||
a.parts.push_back(ArrayType::get(b.getInt64Ty(), 2));
|
||||
a.coerced = a.parts[0];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
if (g_target.windows) {
|
||||
// Microsoft x64: 1, 2, 4 or 8 bytes travel as one integer (even if they hold floats);
|
||||
// anything else is copied by the caller and passed by address
|
||||
|
|
@ -122,7 +156,8 @@ static Plan makePlan(Codegen &cg, const FType &ret, const std::vector<FType> &pa
|
|||
if (a.kind == AbiArg::Expand) {
|
||||
int ni = 0, ns = 0;
|
||||
for (llvm::Type *p : a.parts) (p->isIntegerTy() ? ni : ns)++;
|
||||
if (g_target.windows || (ni <= intRegs && ns <= sseRegs)) {
|
||||
// only System V counts registers here; elsewhere the backend places each part
|
||||
if (g_target.windows || g_target.triple.isAArch64() || (ni <= intRegs && ns <= sseRegs)) {
|
||||
intRegs -= ni;
|
||||
sseRegs -= ns;
|
||||
for (llvm::Type *p : a.parts) types.push_back(p);
|
||||
|
|
@ -143,9 +178,15 @@ static Plan makePlan(Codegen &cg, const FType &ret, const std::vector<FType> &pa
|
|||
return pl;
|
||||
}
|
||||
|
||||
// Is C's plain `char` signed? Yes on x86 and Apple; no on ARM64 Linux.
|
||||
static bool charIsSigned() {
|
||||
return !g_target.triple.isAArch64() || g_target.triple.isOSDarwin() || g_target.triple.isOSWindows();
|
||||
}
|
||||
|
||||
static Attribute::AttrKind extAttr(const FType &t) {
|
||||
if (t.bits() >= 32 || !(t.isInt() || t.kind == FType::Bool || t.kind == FType::Char)) return Attribute::None;
|
||||
return t.isSigned() || t.kind == FType::Char ? Attribute::SExt : Attribute::ZExt;
|
||||
if (t.kind == FType::Char) return charIsSigned() ? Attribute::SExt : Attribute::ZExt;
|
||||
return t.isSigned() ? Attribute::SExt : Attribute::ZExt;
|
||||
}
|
||||
|
||||
// Attributes that tell LLVM how C passes each piece: sret, byval, signext/zeroext.
|
||||
|
|
@ -169,6 +210,7 @@ static AttributeList attrsFor(Codegen &cg, const Plan &pl, const FType &ret, con
|
|||
} else if (a.kind == AbiArg::Indirect) {
|
||||
idx++;
|
||||
} else if (a.kind == AbiArg::Expand) {
|
||||
if (a.stackAlign8) al = al.addParamAttribute(ctx, idx, Attribute::getWithStackAlignment(ctx, Align(8)));
|
||||
idx += a.parts.size();
|
||||
} else {
|
||||
if (Attribute::AttrKind k = extAttr(params[i]); k != Attribute::None) al = al.addParamAttribute(ctx, idx, k);
|
||||
|
|
@ -230,7 +272,7 @@ Value_ Codegen::emitCCall(const CFunc &f, Function *fn, std::vector<Value_> args
|
|||
if (t.kind == FType::F32) x = b.CreateFPExt(x, b.getDoubleTy());
|
||||
else if (t.kind == FType::Str) x = cstr(x);
|
||||
else if (t.kind == FType::Bool || ((t.isInt() || t.kind == FType::Char) && t.bits() < 32))
|
||||
x = b.CreateIntCast(x, b.getInt32Ty(), t.isSigned() || t.kind == FType::Char);
|
||||
x = b.CreateIntCast(x, b.getInt32Ty(), t.isSigned() || (t.kind == FType::Char && charIsSigned()));
|
||||
ll.push_back(x);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue