Skip to content

Commit

Permalink
ABI change: built-in structs, such as arrays, tuples and eithers, are…
Browse files Browse the repository at this point in the history
… now passed as separate parameters.

This massively reduces the need for allocas and stack manipulation.
  • Loading branch information
FeepingCreature committed Jan 4, 2024
1 parent 74acbcb commit f4ba38c
Show file tree
Hide file tree
Showing 16 changed files with 408 additions and 282 deletions.
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ cp -R src build/
echo "Building stage 2..."
FLAGS="$FLAGS -version=LLVMBackend"
# see generation.md
$NEAT $FLAGS -backend=llvm -macro-backend=c -Pcompiler:src -j src/main.nt \
$NEAT $FLAGS -backend=llvm -macro-backend=c -macro-version=oldabi -Pcompiler:src -j src/main.nt \
-o build/neat_stage2
NEAT=build/neat_stage2

Expand Down
4 changes: 2 additions & 2 deletions release-gcc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ OBJECTS=()
# poor man's make -j
for file in intermediate_\${ARCH}/*.c src/runtime.c; do
obj=\${file%.c}.o
gcc \$ARCHFLAG -c -fpic -rdynamic -fno-strict-aliasing \$DMAIN \$file -o \$obj &
gcc \$ARCHFLAG -c -fpic -rdynamic -fno-strict-aliasing \$DMAIN -DNEW_ABI \$file -o \$obj &
OBJECTS+=(\$obj)
if [ \$I -ge \$JOBS ]; then wait -n; fi
I=\$((I+1))
Expand Down Expand Up @@ -176,7 +176,7 @@ OBJECTS=()
# poor man's make -j
for file in intermediate_\${ARCH}/*.c src/runtime.c; do
obj=\${file%.c}.o
gcc \$ARCHFLAG -c -fpic -rdynamic -fno-strict-aliasing \$DMAIN \$file -o \$obj &
gcc \$ARCHFLAG -c -fpic -rdynamic -fno-strict-aliasing \$DMAIN -DNEW_ABI \$file -o \$obj &
OBJECTS+=(\$obj)
if [ \$I -ge \$JOBS ]; then wait -n; fi
I=\$((I+1))
Expand Down
5 changes: 4 additions & 1 deletion src/backend/base.nt
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,10 @@ class BackendStructType : BackendType
{
BackendType[] members;

this(BackendType[] members) {
// if true, can apply backend optimizations that break C ABI.
bool internal;

this(BackendType[] members, this.internal) {
this.members = members;
(int size, int alignment) pair = calcPrefix(this.members);

Expand Down
424 changes: 240 additions & 184 deletions src/backend/c.nt

Large diffs are not rendered by default.

150 changes: 98 additions & 52 deletions src/backend/llvm.nt
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,16 @@ nullable BackendType toAmd64ParamType(BackendType type) {
// only zero-sized types
if (blobs.length == 0) return null;
if (blobs.length == 1) return blobs[0];
return new BackendStructType(blobs);
return new BackendStructType(blobs, internal=false);
}

unittest {
// TODO literal
mut BackendType[] inputArray;
inputArray ~= new BackendFloatType; inputArray ~= new BackendIntType; inputArray ~= new BackendIntType;
auto input = new BackendStructType(inputArray);
auto input = new BackendStructType([new BackendFloatType, new BackendIntType, new BackendIntType], internal=false);
auto output = toAmd64ParamType(input);

mut BackendType[] expectedArray;
expectedArray ~= new BackendLongType; expectedArray ~= new BackendIntType;
assert(output.same(new BackendStructType(expectedArray)));
assert(output.same(new BackendStructType(expectedArray, internal=false)));
}

LLVMTypeRef translateFunc(BackendFunctionPointerType backendFpType, LLVMContextRef context) {
Expand All @@ -152,26 +149,34 @@ LLVMTypeRef translateFunc(BackendFunctionPointerType backendFpType, LLVMContextR
paramTypes ~= LLVMPointerType(
translate(backendFpType.ret, context, true), 0);
}
for (param in backendFpType.params) {
if (param.zeroSize) continue;
if (auto translatedType = toAmd64ParamType(param)) {
void recurse(BackendType type) {
if (type.zeroSize) return;
if (auto struct_ = type.instanceOf(BackendStructType)) {
if (struct_.internal && struct_.size > 16) {
[recurse(member) for member in struct_.members];
return;
}
}
if (auto translatedType = toAmd64ParamType(type)) {
paramTypes ~= translate(translatedType, context, false);
continue;
return;
}
if (passByMemory(param)) {
if (passByMemory(type)) {
paramTypes ~= LLVMPointerType(
translate(param, context, false), 0);
continue;
translate(type, context, false), 0);
return;
}
paramTypes ~= translate(param, context, false);
paramTypes ~= translate(type, context, false);
}
[recurse(param) for param in backendFpType.params];
return LLVMFunctionType(returnType, paramTypes.ptr, cast(int) paramTypes.length, backendFpType.variadic);
}

bool passByMemory(BackendType type) {
// AMD64 ABI, rule 5c: aggregates over 16 bytes that are not SSE are passed in memory (byval).
if (type.instanceOf(BackendStructType) && type.size > 16)
return true;
if (auto struct_ = type.instanceOf(BackendStructType))
if (!struct_.internal && struct_.size > 16)
return true;
return false;
}

Expand Down Expand Up @@ -374,17 +379,26 @@ class LLVMBackendModule : BackendModule {
// first attribute is sret
LLVMAddAttributeAtIndex(llFun, 1, sretTypeAttr);
}
for (i, param in params) {
if (passByMemory(param)) {
// base 1 (0 is return type)
mut int llvmParam = sret + 1;
void recurse(BackendType type) {
if (auto struct_ = type.instanceOf(BackendStructType)) {
if (struct_.internal && struct_.size > 16) {
[recurse(member) for member in struct_.members];
return;
}
}
if (passByMemory(type)) {
auto byvalAttrKind = LLVMGetEnumAttributeKindForName("byval", 5);
assert(byvalAttrKind != 0);
auto byvalTypeAttr = LLVMCreateTypeAttribute(
this.context, byvalAttrKind, translate(param, false));
// base-1 (0 is return type), + 1 for sret parameter
int base = 1 + sret;
LLVMAddAttributeAtIndex(llFun, base + cast(int) i, byvalTypeAttr);
this.context, byvalAttrKind, translate(type, false));
LLVMAddAttributeAtIndex(llFun, llvmParam++, byvalTypeAttr);
} else {
llvmParam++;
}
}
[recurse(param) for param in params];
}

void declareVar(string name, BackendType type, bool define, bool threadlocal) {
Expand Down Expand Up @@ -615,34 +629,48 @@ class LLVMBackendFunction : BackendFunction, PhiCapable {
LLVMPositionBuilderAtEnd(this.entryBuilder, llLabel("entry"));
LLVMPositionBuilderAtEnd(this.builder, llLabel("first"));

// TODO RegDefine[] llArgs
auto llArgs = new LLVMValueRef mut[](this.params.length);
bool sret = passByMemory(this.ret_);
mut int llvmParamId = sret;
for (int i, param in params) {
if (param.size == 0) {
llArgs[i] = null;
continue;
LLVMValueRef getArg(BackendType type) {
int id = llvmParamId;
if (type.size == 0) {
return null;
}
auto llType = this.mod.translate(type, allowVoid=false);
if (auto struct_ = type.instanceOf(BackendStructType)) {
if (struct_.internal && struct_.size > 16) {
auto fields = [getArg(member) for member in struct_.members];
mut auto result = LLVMGetUndef(llType);
mut int translatedIndex = 0;
for (i, field in fields) {
assert(struct_.members[i].size > 0);
int reg = this.nextReg;
result = LLVMBuildInsertValue(
this.builder, result, field, translatedIndex++, "reg$reg".toStringz);
}
return result;
}
}
auto llParam = this.mod.translate(param, allowVoid=false);
if (auto amd64Type = toAmd64ParamType(param)) {
auto llAllocaReg = LLVMBuildAlloca(this.entryBuilder, llParam, "tmparg$i".toStringz);
if (auto amd64Type = toAmd64ParamType(type)) {
auto llAllocaReg = LLVMBuildAlloca(this.entryBuilder, llType, "tmparg$id".toStringz);
auto llAmd64Type = this.mod.translate(amd64Type, false);
auto llAmd64Ptr = this.mod.translate(new BackendPointerType(amd64Type, this.mod.platform), false);
auto llAllocaAmd64Ptr = LLVMBuildBitCast(
this.builder, llAllocaReg, llAmd64Ptr, "argload_alloca$i".toStringz);
this.builder, llAllocaReg, llAmd64Ptr, "argload_alloca$id".toStringz);
auto paramPtr = LLVMGetParam(this.function_, llvmParamId++);
LLVMBuildStore(this.builder, paramPtr, llAllocaAmd64Ptr);
llArgs[i] = LLVMBuildLoad2(this.builder, llParam, llAllocaReg, "argload$i".toStringz);
} else if (passByMemory(param)) {
return LLVMBuildLoad2(this.builder, llType, llAllocaReg, "argload$id".toStringz);
}
if (passByMemory(type)) {
auto paramPtr = LLVMGetParam(this.function_, llvmParamId++);
llArgs[i] = LLVMBuildLoad2(
this.builder, llParam, paramPtr, "arg$i.load".toStringz);
return LLVMBuildLoad2(
this.builder, llType, paramPtr, "arg$id.load".toStringz);
} else {
llArgs[i] = LLVMGetParam(this.function_, llvmParamId++);
return LLVMGetParam(this.function_, llvmParamId++);
}
}
this.llArgs = llArgs.freeze;
// TODO RegDefine[] llArgs
this.llArgs = [getArg(param) for param in params];
}

override string toString() => "LLVMBackendFunction($(this.name))";
Expand Down Expand Up @@ -1032,22 +1060,31 @@ class LLVMBackendFunction : BackendFunction, PhiCapable {
llRetReg = LLVMBuildAlloca(this.entryBuilder, llRetType, "reg$reg.ret".toStringz);
llArgs ~= llRetReg;
}
for (int i, arg in args) {
void recurse(BackendType type, int reg) {
// skip 0-sized parameters
if (params[i].zeroSize) continue;
if (auto amd64Type = toAmd64ParamType(params[i])) {
auto llAlloca = llRegToAlloca(params[i], arg);
if (type.zeroSize) return;
if (auto struct_ = type.instanceOf(BackendStructType)) {
if (struct_.internal && struct_.size > 16) {
[recurse(member, field(type, reg, cast(int) i)) for i, member in struct_.members];
return;
}
}
if (auto amd64Type = toAmd64ParamType(type)) {
auto llAlloca = llRegToAlloca(type, reg);
auto llAmd64Type = this.mod.translate(amd64Type, false);
auto llAmd64PtrType = this.mod.translate(new BackendPointerType(amd64Type, this.mod.platform), false);
auto llAllocaAsAmd64Ptr = LLVMBuildBitCast(
this.builder, llAlloca, llAmd64PtrType, "reg$reg.coerce$i.cast".toStringz);
this.builder, llAlloca, llAmd64PtrType, "reg$reg.coerce$(llArgs.length).cast".toStringz);
llArgs ~= LLVMBuildLoad2(this.builder, llAmd64Type, llAllocaAsAmd64Ptr, "reg$reg.load".toStringz);
} else if (passByMemory(params[i])) {
llArgs ~= llRegToAlloca(params[i], arg);
return;
}
if (passByMemory(type)) {
llArgs ~= llRegToAlloca(type, reg);
} else {
llArgs ~= llReg(arg);
llArgs ~= llReg(reg);
}
}
[recurse(params[i], arg) for i, arg in args];

mut LLVMValueRef llFun;

Expand Down Expand Up @@ -1085,17 +1122,26 @@ class LLVMBackendFunction : BackendFunction, PhiCapable {
LLVMAddCallSiteAttribute(llCall, 1, sretTypeAttr);
}

for (int i, param in params) {
if (passByMemory(param)) {
// base-1 (0 is return type), + 1 for sret parameter
mut int llvmParam = sret + 1;
void recurse(BackendType type) {
if (auto struct_ = type.instanceOf(BackendStructType)) {
if (struct_.internal && struct_.size > 16) {
[recurse(member) for member in struct_.members];
return;
}
}
if (passByMemory(type)) {
auto byvalAttrKind = LLVMGetEnumAttributeKindForName("byval", 5);
assert(byvalAttrKind != 0);
auto byvalTypeAttr = LLVMCreateTypeAttribute(
this.mod.context, byvalAttrKind, this.mod.translate(param, false));
// base-1 (0 is return type), + 1 for sret parameter
int base = 1 + sret;
LLVMAddCallSiteAttribute(llCall, base + cast(int) i, byvalTypeAttr);
this.mod.context, byvalAttrKind, this.mod.translate(type, false));
LLVMAddCallSiteAttribute(llCall, llvmParam++, byvalTypeAttr);
} else {
llvmParam++;
}
}
[recurse(param) for param in params];

if (sret) {
this.defRegTransparentPtr(reg, retType, llRetReg);
Expand Down
1 change: 1 addition & 0 deletions src/main.nt
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,7 @@ long long int compiler_hash_mult() { return $(mult)LL; }
flags ~= " -DNEAT_NO_MAIN";
else
flags ~= " -DMAIN=$(mainName?)";
flags ~= " -DNEW_ABI";
string runtime = execPath ~ "/src/runtime.c";
string cmd = "$compilerCmd$flags$fileArgs $runtime -o $(options.output)$linkerFlags";
if (settings.platform.platformFlags.verbose) {
Expand Down
2 changes: 1 addition & 1 deletion src/neat/array.nt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Array : Type
// No Element type here; there would be an infinite recursion with self-referential types like JSONValue.
types[1] = platform.voidp;
types[2] = platform.voidp;
this.backendType = new BackendStructType(types.freeze);
this.backendType = new BackendStructType(types.freeze, internal=true);
}

Type elementType() {
Expand Down
2 changes: 1 addition & 1 deletion src/neat/class_.nt
Original file line number Diff line number Diff line change
Expand Up @@ -1949,7 +1949,7 @@ class NewClassExpression : Expression
// oh boy!
BackendType voidp = once (new Pointer(new Void)).emit(output.platform);
BackendType sizeT = output.platform.nativeWordType;
auto basicClassStruct = new BackendStructType([voidp, sizeT]);
auto basicClassStruct = new BackendStructType([voidp, sizeT], internal=true);

int classDataSize = this.classType.class_.dataStruct.emit(output.platform).size;
int classInfoPtr = (new ClassInfo(classType)).emit(output);
Expand Down
2 changes: 1 addition & 1 deletion src/neat/delegate_.nt
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class Delegate : Type
for (i, param in this.params) params[i + 1] = param.emit(platform);
auto fp = new BackendFunctionPointerType(
this.ret.emit(platform), params.freeze, variadic=false, platform);
return new BackendStructType([platform.voidp, fp]);
return new BackendStructType([platform.voidp, fp], internal=true);
}

override bool same(Type other) {
Expand Down
2 changes: 1 addition & 1 deletion src/neat/either.nt
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class Either : Type, Hashable
if (size <= 4 && alignment <= 4) parts[1] = new BackendIntType;
else if (size <= 8 && alignment <= 8) parts[1] = new BackendLongType;
else parts[1] = new BackendSpacerType(size, alignment);
return new BackendStructType(parts.freeze);
return new BackendStructType(parts.freeze, internal=true);
}

override (nullable Expression | Error) implicitConvertFrom(
Expand Down
4 changes: 2 additions & 2 deletions src/neat/function_.nt
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ class StackframePlanner
private void capture(int id, Platform platform) {
int max(int a, int b) => a if a > b else b;
mut int id = id;
auto frameType = new BackendStructType([type.emit(platform) for type in variables[id].frame]);
auto frameType = new BackendStructType([type.emit(platform) for type in variables[id].frame], internal=true);
this.maxSize = this.maxSize.max(frameType.size);
this.maxAlignment = this.maxAlignment.max(frameType.alignment);
while (!variables[id].captured) {
Expand All @@ -364,7 +364,7 @@ class StackframePlanner
int fieldPtr(Expression framePointer, int frameDepth, int varId, Generator output) {
auto var = this.variables[varId];
if (var.captured) {
auto frameType = new BackendStructType([type.emit(output.platform) for type in var.frame]);
auto frameType = new BackendStructType([type.emit(output.platform) for type in var.frame], internal=true);
int structPtrReg = framePointer.emit(output);
return output.fun.fieldOffset(frameType, structPtrReg, cast(int) var.frame.length - 1);
} else {
Expand Down
3 changes: 1 addition & 2 deletions src/neat/parser.nt
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,7 @@ final class ParserImpl : Parser
return false;
}

override void strip()
{
override void strip() {
current = stripTargets.ptr[current];
}

Expand Down
2 changes: 1 addition & 1 deletion src/neat/struct_.nt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class Struct : Type, Hashable
memberTypes[i] = member.type.emit(platform);
}
backendCache.platform = platform;
backendCache.type = new BackendStructType(memberTypes.freeze);
backendCache.type = new BackendStructType(memberTypes.freeze, internal=false);
// FIXME figure out why references go to zero
neat_runtime_refcount_inc2("workaround", &(cast(size_t*) backendCache.type)[1]);
}
Expand Down
2 changes: 1 addition & 1 deletion src/neat/tuples.nt
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class TupleType : Type, Hashable
}

override BackendType emit(Platform platform) {
return new BackendStructType([member.type.emit(platform) for member in members]);
return new BackendStructType([member.type.emit(platform) for member in members], internal=true);
}

override bool same(Type type) {
Expand Down
2 changes: 1 addition & 1 deletion src/neat/vectors.nt
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class VectorType : Type, Hashable
BackendType elementType = this.elementType.emit(platform);
mut BackendType[] members;
for (i in 0 .. length) members ~= elementType;
return new BackendStructType(members);
return new BackendStructType(members, internal=true);
}

override bool same(Type other) {
Expand Down
Loading

0 comments on commit f4ba38c

Please sign in to comment.