Skip to content

Commit 2a93503

Browse files
authored
Merge pull request #68 from OpenVADL/feature/calling-convention-abi
frontend: Implement lowering of calling convention
2 parents 71cf398 + 163a3f8 commit 2a93503

24 files changed

+942
-457
lines changed

vadl/main/resources/templates/lcb/llvm/lib/Target/RegisterInfo.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ BitVector [(${namespace})]RegisterInfo::getReservedRegs(const MachineFunction &M
4848
markSuperRegs(Reserved, [(${namespace})]::[(${framePointer})]); // frame pointer
4949
markSuperRegs(Reserved, [(${namespace})]::[(${stackPointer})]); // stack pointer
5050
markSuperRegs(Reserved, [(${namespace})]::[(${globalPointer})]); // global pointer
51+
52+
[# th:if="hasThreadPointer" ]
5153
markSuperRegs(Reserved, [(${namespace})]::[(${threadPointer})]); // thread pointer
54+
[/]
5255

5356
[# th:each="constraint : ${constraints}" ]
5457
markSuperRegs(Reserved, [(${namespace})]::[(${constraint.registerFile})][(${constraint.index})]);

vadl/main/vadl/ast/Definition.java

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3475,14 +3475,14 @@ enum SeqKind {
34753475
class SpecialPurposeRegisterDefinition extends Definition {
34763476

34773477
Purpose purpose;
3478-
Identifier aliasName;
3478+
List<SequenceCallExpr> exprs;
34793479
SourceLocation loc;
34803480

34813481
SpecialPurposeRegisterDefinition(Purpose purpose,
3482-
Identifier aliasName,
3482+
List<SequenceCallExpr> sequence,
34833483
SourceLocation loc) {
34843484
this.purpose = purpose;
3485-
this.aliasName = aliasName;
3485+
this.exprs = sequence;
34863486
this.loc = loc;
34873487
}
34883488

@@ -3507,7 +3507,9 @@ void prettyPrint(int indent, StringBuilder builder) {
35073507
builder.append(prettyIndentString(indent));
35083508
builder.append(purpose.keywords);
35093509
builder.append(" = ");
3510-
builder.append(aliasName);
3510+
exprs.forEach(e -> {
3511+
e.prettyPrint(indent + 1, builder);
3512+
});
35113513
}
35123514

35133515
@Override
@@ -3519,12 +3521,18 @@ public boolean equals(Object o) {
35193521
return false;
35203522
}
35213523
SpecialPurposeRegisterDefinition that = (SpecialPurposeRegisterDefinition) o;
3522-
return purpose == that.purpose && aliasName.equals(that.aliasName);
3524+
return purpose == that.purpose && Objects.equals(exprs, that.exprs);
35233525
}
35243526

35253527
@Override
35263528
public int hashCode() {
3527-
return Objects.hash(purpose, aliasName);
3529+
return Objects.hash(purpose, exprs);
3530+
}
3531+
3532+
enum Occurrence {
3533+
OPTIONAL,
3534+
ONE,
3535+
AT_LEAST_ONE
35283536
}
35293537

35303538
enum Purpose {
@@ -3543,6 +3551,41 @@ enum Purpose {
35433551
Purpose(String keywords) {
35443552
this.keywords = keywords;
35453553
}
3554+
3555+
/**
3556+
* Determines how many arguments are allowed.
3557+
* function value = a{0..1} -> ok
3558+
* stack pointer = a{0..1} -> not ok
3559+
*/
3560+
public static final Map<Purpose, Occurrence> numberOfExpectedArguments;
3561+
3562+
/**
3563+
* Determines how often a definition is allowed in the ABI.
3564+
*/
3565+
public static final Map<Purpose, Occurrence> numberOfOccurrencesAbi;
3566+
3567+
static {
3568+
numberOfExpectedArguments = Map.of(Purpose.STACK_POINTER, Occurrence.ONE,
3569+
Purpose.RETURN_ADDRESS, Occurrence.ONE,
3570+
Purpose.GLOBAL_POINTER, Occurrence.ONE,
3571+
Purpose.FRAME_POINTER, Occurrence.ONE,
3572+
Purpose.THREAD_POINTER, Occurrence.ONE,
3573+
Purpose.RETURN_VALUE, Occurrence.AT_LEAST_ONE,
3574+
Purpose.CALLER_SAVED, Occurrence.AT_LEAST_ONE,
3575+
Purpose.CALLEE_SAVED, Occurrence.AT_LEAST_ONE,
3576+
Purpose.FUNCTION_ARGUMENT, Occurrence.AT_LEAST_ONE);
3577+
3578+
3579+
numberOfOccurrencesAbi = Map.of(Purpose.STACK_POINTER, Occurrence.ONE,
3580+
Purpose.RETURN_ADDRESS, Occurrence.ONE,
3581+
Purpose.GLOBAL_POINTER, Occurrence.ONE,
3582+
Purpose.FRAME_POINTER, Occurrence.ONE,
3583+
Purpose.THREAD_POINTER, Occurrence.OPTIONAL,
3584+
Purpose.RETURN_VALUE, Occurrence.ONE,
3585+
Purpose.CALLER_SAVED, Occurrence.ONE,
3586+
Purpose.CALLEE_SAVED, Occurrence.ONE,
3587+
Purpose.FUNCTION_ARGUMENT, Occurrence.ONE);
3588+
}
35463589
}
35473590
}
35483591

vadl/main/vadl/ast/MacroExpander.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ public Definition visit(AbiSequenceDefinition definition) {
713713
@Override
714714
public Definition visit(SpecialPurposeRegisterDefinition definition) {
715715
return new SpecialPurposeRegisterDefinition(
716-
definition.purpose, definition.aliasName, copyLoc(definition.loc)
716+
definition.purpose, definition.exprs, copyLoc(definition.loc)
717717
).withAnnotations(expandAnnotations(definition.annotations));
718718
}
719719

vadl/main/vadl/ast/ParserUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,9 +685,9 @@ static List<SequenceCallExpr> expandSequenceCalls(Parser parser, List<SequenceCa
685685
} else {
686686
reportError(parser, "Unknown index type " + callExpr.range, callExpr.range.location());
687687
}
688-
for (; !start.equals(end); start = start.add(BigInteger.valueOf(end.compareTo(start)))) {
688+
for (BigInteger i = start; i.compareTo(end) <= 0; i = i.add(BigInteger.ONE)) {
689689
expandedCalls.add(
690-
new SequenceCallExpr(new Identifier(callExpr.target.name + start, callExpr.loc), null,
690+
new SequenceCallExpr(new Identifier(callExpr.target.name + i, callExpr.loc), null,
691691
callExpr.loc));
692692
}
693693
}

vadl/main/vadl/ast/TypeChecker.java

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -750,26 +750,42 @@ public Void visit(GroupDefinition groupDefinition) {
750750
public Void visit(ApplicationBinaryInterfaceDefinition definition) {
751751
definition.definitions.forEach(this::check);
752752

753-
// Check whether there exists just one special purpose register.
754-
for (var purpose : Set.of(SpecialPurposeRegisterDefinition.Purpose.STACK_POINTER,
755-
SpecialPurposeRegisterDefinition.Purpose.RETURN_ADDRESS,
756-
SpecialPurposeRegisterDefinition.Purpose.GLOBAL_POINTER,
757-
SpecialPurposeRegisterDefinition.Purpose.FRAME_POINTER,
758-
SpecialPurposeRegisterDefinition.Purpose.THREAD_POINTER)) {
753+
// Check the number of occurrences in the ABI.
754+
for (var entry : SpecialPurposeRegisterDefinition.Purpose.numberOfOccurrencesAbi.entrySet()) {
755+
var purpose = entry.getKey();
759756
var registers = definition.definitions.stream().filter(
760757
x -> x instanceof SpecialPurposeRegisterDefinition specialPurposeRegisterDefinition
761758
&& specialPurposeRegisterDefinition.purpose == purpose)
762759
.toList();
763760

764-
if (registers.size() > 1) {
765-
throw Diagnostic.error(
766-
"Multiple " + purpose.name() + " registers were declared but only was expected",
767-
SourceLocation.join(registers.stream().map(Node::sourceLocation).toList())).build();
768-
}
769-
770-
if (registers.isEmpty()) {
771-
throw Diagnostic.error(
772-
"No register purpose was defined for " + purpose.name(), definition.loc).build();
761+
switch (entry.getValue()) {
762+
case ONE -> {
763+
if (registers.isEmpty()) {
764+
throw Diagnostic.error(
765+
"No " + purpose.name() + " registers were declared but one was expected",
766+
definition.sourceLocation()).build();
767+
} else if (registers.size() != 1) {
768+
throw Diagnostic.error(
769+
"Multiple " + purpose.name() + " registers were declared but only one was expected",
770+
SourceLocation.join(registers.stream().map(Node::sourceLocation).toList())).build();
771+
}
772+
}
773+
case OPTIONAL -> {
774+
if (!(registers.isEmpty() || registers.size() == 1)) {
775+
throw Diagnostic.error(
776+
"Multiple " + purpose.name()
777+
+ " registers were declared but zero or one was expected",
778+
SourceLocation.join(registers.stream().map(Node::sourceLocation).toList())).build();
779+
}
780+
}
781+
case AT_LEAST_ONE -> {
782+
if (registers.isEmpty()) {
783+
throw Diagnostic.error(
784+
"Zero " + purpose.name() + " registers were declared but at least one was expected",
785+
definition.sourceLocation()).build();
786+
}
787+
}
788+
default -> throw new RuntimeException("enum variant not handled");
773789
}
774790
}
775791

@@ -783,7 +799,7 @@ public Void visit(ApplicationBinaryInterfaceDefinition definition) {
783799
if (pseudoInstructions.size() > 1) {
784800
throw Diagnostic.error(
785801
"Multiple " + kind.name()
786-
+ " pseudo instructions were declared but only was expected",
802+
+ " pseudo instructions were declared but only one was expected",
787803
SourceLocation.join(pseudoInstructions.stream().map(Node::sourceLocation).toList()))
788804
.build();
789805
}
@@ -1382,7 +1398,31 @@ public Void visit(AbiSequenceDefinition definition) {
13821398

13831399
@Override
13841400
public Void visit(SpecialPurposeRegisterDefinition definition) {
1385-
// Isn't type checked on purpose because there is nothing to type check.
1401+
// Check whether the number of registers is correct.
1402+
// There can be only one stack pointer. However, there might be multiple argument registers.
1403+
var actual =
1404+
SpecialPurposeRegisterDefinition.Purpose.numberOfExpectedArguments.get(definition.purpose);
1405+
1406+
if (actual == null) {
1407+
throw Diagnostic.error("Cannot determine number of expected registers",
1408+
definition.sourceLocation()).build();
1409+
}
1410+
1411+
if (actual == SpecialPurposeRegisterDefinition.Occurrence.ONE) {
1412+
if (definition.exprs.size() != 1) {
1413+
throw Diagnostic.error("Number of registers is incorrect. This definition expects only one",
1414+
definition.sourceLocation()).build();
1415+
}
1416+
}
1417+
1418+
if (actual == SpecialPurposeRegisterDefinition.Occurrence.ONE) {
1419+
if (definition.exprs.isEmpty()) {
1420+
throw Diagnostic.error(
1421+
"Number of registers is incorrect. This definition expects at least one.",
1422+
definition.sourceLocation()).build();
1423+
}
1424+
}
1425+
13861426
return null;
13871427
}
13881428

vadl/main/vadl/ast/ViamLowering.java

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ public Optional<vadl.viam.Definition> visit(ApplicationBinaryInterfaceDefinition
231231
var id = generateIdentifier(definition.viamId, definition.identifier());
232232
var aliasLookup = aliasLookupTable(definition.definitions);
233233

234+
// Special Registers
235+
234236
var stackPointerDef = getSpecialPurposeRegisterDefinition(definition.definitions,
235237
SpecialPurposeRegisterDefinition.Purpose.STACK_POINTER);
236238
var returnAddressDef =
@@ -240,14 +242,31 @@ public Optional<vadl.viam.Definition> visit(ApplicationBinaryInterfaceDefinition
240242
SpecialPurposeRegisterDefinition.Purpose.GLOBAL_POINTER);
241243
var framePtrDef = getSpecialPurposeRegisterDefinition(definition.definitions,
242244
SpecialPurposeRegisterDefinition.Purpose.FRAME_POINTER);
243-
var threadPtrDef = getSpecialPurposeRegisterDefinition(definition.definitions,
244-
SpecialPurposeRegisterDefinition.Purpose.THREAD_POINTER);
245-
246-
var stackPointer = mapToRegisterRef(aliasLookup, stackPointerDef);
247-
var returnAddress = mapToRegisterRef(aliasLookup, returnAddressDef);
248-
var globalPtr = mapToRegisterRef(aliasLookup, globalPtrDef);
249-
var framePtr = mapToRegisterRef(aliasLookup, framePtrDef);
250-
var threadPtr = mapToRegisterRef(aliasLookup, threadPtrDef);
245+
var threadPtr = getOptionalSpecialPurposeRegisterDefinition(definition.definitions,
246+
SpecialPurposeRegisterDefinition.Purpose.THREAD_POINTER)
247+
.map(def -> mapSingleSpecialPurposeRegisterDef(aliasLookup, def));
248+
249+
var stackPointer = mapSingleSpecialPurposeRegisterDef(aliasLookup, stackPointerDef);
250+
var returnAddress = mapSingleSpecialPurposeRegisterDef(aliasLookup, returnAddressDef);
251+
var globalPtr = mapSingleSpecialPurposeRegisterDef(aliasLookup, globalPtrDef);
252+
var framePtr = mapSingleSpecialPurposeRegisterDef(aliasLookup, framePtrDef);
253+
254+
// Calling Convention
255+
var returnValueDef = getSpecialPurposeRegisterDefinition(definition.definitions,
256+
SpecialPurposeRegisterDefinition.Purpose.RETURN_VALUE);
257+
var functionArgumentDef = getSpecialPurposeRegisterDefinition(definition.definitions,
258+
SpecialPurposeRegisterDefinition.Purpose.FUNCTION_ARGUMENT);
259+
var callerSavedDef = getSpecialPurposeRegisterDefinition(definition.definitions,
260+
SpecialPurposeRegisterDefinition.Purpose.CALLER_SAVED);
261+
var calleeSavedDef = getSpecialPurposeRegisterDefinition(definition.definitions,
262+
SpecialPurposeRegisterDefinition.Purpose.CALLEE_SAVED);
263+
264+
var returnValues = mapSpecialPurposeRegistersDef(aliasLookup, returnValueDef);
265+
var functionArguments = mapSpecialPurposeRegistersDef(aliasLookup, functionArgumentDef);
266+
var callerSaved = mapSpecialPurposeRegistersDef(aliasLookup, callerSavedDef);
267+
var calleeSaved = mapSpecialPurposeRegistersDef(aliasLookup, calleeSavedDef);
268+
269+
// Pseudo Instructions
251270

252271
var pseudoRetInstrDef = getAbiPseudoInstruction(definition.definitions,
253272
AbiPseudoInstructionDefinition.Kind.RETURN);
@@ -260,6 +279,8 @@ public Optional<vadl.viam.Definition> visit(ApplicationBinaryInterfaceDefinition
260279
var pseudoCall = (PseudoInstruction) fetch(pseudoCallInstrDef).orElseThrow();
261280
var pseudoLocalAddressLoad = (PseudoInstruction) fetch(pseudoLocalAddressLoadDef).orElseThrow();
262281

282+
// Aliases
283+
263284
Map<Pair<RegisterFile, Integer>, List<Abi.RegisterAlias>> aliases =
264285
aliasLookup.entrySet()
265286
.stream()
@@ -293,10 +314,10 @@ public Optional<vadl.viam.Definition> visit(ApplicationBinaryInterfaceDefinition
293314
globalPtr,
294315
threadPtr,
295316
aliases,
296-
Collections.emptyList(),
297-
Collections.emptyList(),
298-
Collections.emptyList(),
299-
Collections.emptyList(),
317+
callerSaved,
318+
calleeSaved,
319+
functionArguments,
320+
returnValues,
300321
pseudoRet,
301322
pseudoCall,
302323
pseudoLocalAddressLoad,
@@ -1257,16 +1278,39 @@ public Optional<vadl.viam.Definition> visit(
12571278
return Optional.ofNullable(pseudoInstructionDefinition).flatMap(this::fetch);
12581279
}
12591280

1281+
/**
1282+
* Maps a {@link SpecialPurposeRegisterDefinition} to a {@link Abi.RegisterRef}.
1283+
* It expects only one register in the {@link SpecialPurposeRegisterDefinition}. Otherwise,
1284+
* it will throw an error.
1285+
*/
1286+
private Abi.RegisterRef mapSingleSpecialPurposeRegisterDef(
1287+
Map<Identifier, Expr> aliasLookup,
1288+
SpecialPurposeRegisterDefinition specialPurposeRegisterDef) {
1289+
var identifier = specialPurposeRegisterDef.exprs.stream().findFirst().orElseThrow().target;
1290+
return mapToRegisterRef(aliasLookup, identifier);
1291+
}
1292+
1293+
/**
1294+
* Maps a {@link SpecialPurposeRegisterDefinition} to a list of {@link Abi.RegisterRef}.
1295+
*/
1296+
private List<Abi.RegisterRef> mapSpecialPurposeRegistersDef(
1297+
Map<Identifier, Expr> aliasLookup,
1298+
SpecialPurposeRegisterDefinition specialPurposeRegisterDef) {
1299+
return specialPurposeRegisterDef.exprs.stream()
1300+
.map(x -> mapToRegisterRef(aliasLookup, x.target))
1301+
.toList();
1302+
}
1303+
12601304
/**
12611305
* Maps the aliases {@code alias register zero = X(0)} to {@link Abi.RegisterRef} to be
12621306
* used in {@link Abi}.
12631307
*/
12641308
private Abi.RegisterRef mapToRegisterRef(
12651309
Map<Identifier, Expr> aliasLookup,
1266-
SpecialPurposeRegisterDefinition specialPurposeRegisterDef) {
1267-
var expr = ensureNonNull(aliasLookup.get(specialPurposeRegisterDef.aliasName),
1310+
Identifier identifier) {
1311+
var expr = ensureNonNull(aliasLookup.get(identifier),
12681312
() -> Diagnostic.error("Cannot alias for register definition",
1269-
specialPurposeRegisterDef.aliasName.sourceLocation()));
1313+
identifier.sourceLocation()));
12701314
var pair = getRegisterFile(expr);
12711315
var registerFile = pair.left();
12721316
var index = pair.right();
@@ -1319,12 +1363,21 @@ private Map<Identifier, Expr> aliasLookupTable(List<Definition> definitions) {
13191363
*/
13201364
private SpecialPurposeRegisterDefinition getSpecialPurposeRegisterDefinition(
13211365
List<Definition> definitions, SpecialPurposeRegisterDefinition.Purpose purpose) {
1366+
return getOptionalSpecialPurposeRegisterDefinition(definitions, purpose).get();
1367+
}
1368+
1369+
/**
1370+
* Extracts a {@link SpecialPurposeRegisterDefinition} with the given {@code purpose}.
1371+
* It is ok to find no definition.
1372+
*/
1373+
private Optional<SpecialPurposeRegisterDefinition> getOptionalSpecialPurposeRegisterDefinition(
1374+
List<Definition> definitions, SpecialPurposeRegisterDefinition.Purpose purpose) {
13221375
var registers = definitions
13231376
.stream()
13241377
.filter(x -> x instanceof SpecialPurposeRegisterDefinition y && y.purpose == purpose)
13251378
.toList();
13261379

1327-
return (SpecialPurposeRegisterDefinition) registers.stream().findFirst().get();
1380+
return registers.stream().findFirst().map(x -> (SpecialPurposeRegisterDefinition) x);
13281381
}
13291382

13301383

0 commit comments

Comments
 (0)