From a0fb625e2048c9cce946191d26f16fc5e39fd99b Mon Sep 17 00:00:00 2001 From: vegecode Date: Mon, 29 Apr 2019 21:15:57 -0500 Subject: [PATCH] Add arm interrupt calling conventions --- src/all_types.hpp | 6 ++++ src/analyze.cpp | 18 +++++++++++ src/ast_render.cpp | 6 ++++ src/codegen.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++---- src/ir.cpp | 6 ++++ src/parser.cpp | 24 +++++++++++++++ src/tokenizer.cpp | 12 ++++++++ src/tokenizer.hpp | 6 ++++ 8 files changed, 147 insertions(+), 5 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 5a5c1cfda40c..b0e7cfdcea4f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -479,6 +479,12 @@ enum CallingConvention { CallingConventionNaked, CallingConventionStdcall, CallingConventionAsync, + CallingConventionArmInterruptGeneric, + CallingConventionArmInterruptIRQ, + CallingConventionArmInterruptFIQ, + CallingConventionArmInterruptSWI, + CallingConventionArmInterruptABORT, + CallingConventionArmInterruptUNDEF, }; struct AstNodeFnProto { diff --git a/src/analyze.cpp b/src/analyze.cpp index aa510003b8d3..158bc79d43bf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -796,6 +796,12 @@ const char *calling_convention_name(CallingConvention cc) { case CallingConventionNaked: return "nakedcc"; case CallingConventionStdcall: return "stdcallcc"; case CallingConventionAsync: return "async"; + case CallingConventionArmInterruptGeneric: return "arminterruptcc"; + case CallingConventionArmInterruptIRQ: return "armirqcc"; + case CallingConventionArmInterruptFIQ: return "armfiqcc"; + case CallingConventionArmInterruptSWI: return "armswicc"; + case CallingConventionArmInterruptABORT: return "armabortcc"; + case CallingConventionArmInterruptUNDEF: return "armundefcc"; } zig_unreachable(); } @@ -808,6 +814,12 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) { case CallingConventionNaked: return "nakedcc "; case CallingConventionStdcall: return "stdcallcc "; case CallingConventionAsync: return "async "; + case CallingConventionArmInterruptGeneric: return "arminterruptcc "; + case CallingConventionArmInterruptIRQ: return "armirqcc "; + case CallingConventionArmInterruptFIQ: return "armfiqcc "; + case CallingConventionArmInterruptSWI: return "armswicc "; + case CallingConventionArmInterruptABORT: return "armabortcc "; + case CallingConventionArmInterruptUNDEF: return "armundefcc "; } zig_unreachable(); } @@ -821,6 +833,12 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { case CallingConventionCold: case CallingConventionNaked: case CallingConventionStdcall: + case CallingConventionArmInterruptGeneric: + case CallingConventionArmInterruptIRQ: + case CallingConventionArmInterruptFIQ: + case CallingConventionArmInterruptSWI: + case CallingConventionArmInterruptABORT: + case CallingConventionArmInterruptUNDEF: return false; } zig_unreachable(); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 95ae216f7040..4703aed5d264 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -120,6 +120,12 @@ static const char *export_string(bool is_export) { // case CallingConventionCold: return "coldcc "; // case CallingConventionNaked: return "nakedcc "; // case CallingConventionStdcall: return "stdcallcc "; +// case CallingConventionArmInterruptGeneric: return "arminterruptcc"; +// case CallingConventionArmInterruptIRQ: return "armirqcc"; +// case CallingConventionArmInterruptFIQ: return "armfiqcc"; +// case CallingConventionArmInterruptSWI: return "armswicc"; +// case CallingConventionArmInterruptABORT: return "armabortcc"; +// case CallingConventionArmInterruptUNDEF: return "armundefcc"; // } // zig_unreachable(); //} diff --git a/src/codegen.cpp b/src/codegen.cpp index 8d39c60ef7d0..7bbf19471dc5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -379,6 +379,12 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { } break; case CallingConventionNaked: + case CallingConventionArmInterruptGeneric: + case CallingConventionArmInterruptIRQ: + case CallingConventionArmInterruptFIQ: + case CallingConventionArmInterruptSWI: + case CallingConventionArmInterruptABORT: + case CallingConventionArmInterruptUNDEF: zig_unreachable(); case CallingConventionStdcall: // stdcall calling convention only works on x86. @@ -447,6 +453,12 @@ static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkag static bool cc_want_sret_attr(CallingConvention cc) { switch (cc) { case CallingConventionNaked: + case CallingConventionArmInterruptGeneric: + case CallingConventionArmInterruptIRQ: + case CallingConventionArmInterruptFIQ: + case CallingConventionArmInterruptSWI: + case CallingConventionArmInterruptABORT: + case CallingConventionArmInterruptUNDEF: zig_unreachable(); case CallingConventionC: case CallingConventionCold: @@ -549,11 +561,33 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { break; } - if (cc == CallingConventionNaked) { - addLLVMFnAttr(fn_table_entry->llvm_value, "naked"); - } else { - LLVMSetFunctionCallConv(fn_table_entry->llvm_value, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc)); - } + switch (cc) { + case CallingConventionNaked: + addLLVMFnAttr(fn_table_entry->llvm_value, "naked"); + break; + case CallingConventionArmInterruptGeneric: + addLLVMFnAttrStr(fn_table_entry->llvm_value, "interrupt", ""); + break; + case CallingConventionArmInterruptIRQ: + addLLVMFnAttrStr(fn_table_entry->llvm_value, "interrupt", "IRQ"); + break; + case CallingConventionArmInterruptFIQ: + addLLVMFnAttrStr(fn_table_entry->llvm_value, "interrupt", "FIQ"); + break; + case CallingConventionArmInterruptSWI: + addLLVMFnAttrStr(fn_table_entry->llvm_value, "interrupt", "SWI"); + break; + case CallingConventionArmInterruptABORT: + addLLVMFnAttrStr(fn_table_entry->llvm_value, "interrupt", "ABORT"); + break; + case CallingConventionArmInterruptUNDEF: + addLLVMFnAttrStr(fn_table_entry->llvm_value, "interrupt", "UNDEF"); + break; + default: + LLVMSetFunctionCallConv(fn_table_entry->llvm_value, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc)); + break; + }; + if (cc == CallingConventionAsync) { addLLVMFnAttr(fn_table_entry->llvm_value, "optnone"); addLLVMFnAttr(fn_table_entry->llvm_value, "noinline"); @@ -594,6 +628,24 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { if (fn_table_entry->alignstack_value != 0) { addLLVMFnAttrInt(fn_table_entry->llvm_value, "alignstack", fn_table_entry->alignstack_value); + } else { + switch (cc) { + // AAPCS guarantees that sp will be 8-byte aligned on any public interface, + // however this is not necessarily true on taking any interrupt. Instruct + // the backend to perform a realignment as part of the function prologue. + // NOTE: Clang only does this for AAPCS, but it won't hurt APCS, and + // can be overriden by @setAlignStack if required by user + case CallingConventionArmInterruptGeneric: + case CallingConventionArmInterruptIRQ: + case CallingConventionArmInterruptFIQ: + case CallingConventionArmInterruptSWI: + case CallingConventionArmInterruptABORT: + case CallingConventionArmInterruptUNDEF: + addLLVMFnAttrInt(fn_table_entry->llvm_value, "alignstack", 8); + break; + default: + break; + }; } addLLVMFnAttr(fn_table_entry->llvm_value, "nounwind"); @@ -7693,6 +7745,12 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " Naked,\n" " Stdcall,\n" " Async,\n" + " ArmInterruptGeneric,\n" + " ArmInterruptIRQ,\n" + " ArmInterruptFIQ,\n" + " ArmInterruptSWI,\n" + " ArmInterruptABORT,\n" + " ArmInterruptUNDEF,\n" " };\n" "\n" " pub const FnArg = struct {\n" @@ -7759,6 +7817,12 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { assert(CallingConventionNaked == 3); assert(CallingConventionStdcall == 4); assert(CallingConventionAsync == 5); + assert(CallingConventionArmInterruptGeneric == 6); + assert(CallingConventionArmInterruptIRQ == 7); + assert(CallingConventionArmInterruptFIQ == 8); + assert(CallingConventionArmInterruptSWI == 9); + assert(CallingConventionArmInterruptABORT == 10); + assert(CallingConventionArmInterruptUNDEF == 11); assert(FnInlineAuto == 0); assert(FnInlineAlways == 1); diff --git a/src/ir.cpp b/src/ir.cpp index 91f931462e54..478b39118342 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13633,6 +13633,12 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case CallingConventionNaked: case CallingConventionCold: case CallingConventionStdcall: + case CallingConventionArmInterruptGeneric: + case CallingConventionArmInterruptIRQ: + case CallingConventionArmInterruptFIQ: + case CallingConventionArmInterruptSWI: + case CallingConventionArmInterruptABORT: + case CallingConventionArmInterruptUNDEF: add_fn_export(ira->codegen, fn_entry, symbol_name, global_linkage_id, cc == CallingConventionC); break; } diff --git a/src/parser.cpp b/src/parser.cpp index 9e72954b6419..5bd9040b7147 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2032,6 +2032,30 @@ static Optional ast_parse_fn_cc(ParseContext *pc) { expect_token(pc, TokenIdCmpGreaterThan); return Optional::some(res); } + if (eat_token_if(pc, TokenIdKeywordArmInterruptCC) != nullptr) { + res.cc = CallingConventionArmInterruptGeneric; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordArmIrqCC) != nullptr) { + res.cc = CallingConventionArmInterruptIRQ; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordArmFiqCC) != nullptr) { + res.cc = CallingConventionArmInterruptFIQ; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordArmSwiCC) != nullptr) { + res.cc = CallingConventionArmInterruptSWI; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordArmAbortCC) != nullptr) { + res.cc = CallingConventionArmInterruptABORT; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordArmUndefCC) != nullptr) { + res.cc = CallingConventionArmInterruptUNDEF; + return Optional::some(res); + } return Optional::none(); } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 53554d1096d0..d4d6fd8c475b 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -110,6 +110,12 @@ static const struct ZigKeyword zig_keywords[] = { {"allowzero", TokenIdKeywordAllowZero}, {"and", TokenIdKeywordAnd}, {"anyerror", TokenIdKeywordAnyerror}, + {"arminterruptcc", TokenIdKeywordArmInterruptCC}, + {"armirqcc", TokenIdKeywordArmIrqCC}, + {"armfiqcc", TokenIdKeywordArmFiqCC}, + {"armswicc", TokenIdKeywordArmSwiCC}, + {"armabortcc", TokenIdKeywordArmAbortCC}, + {"armundefcc", TokenIdKeywordArmUndefCC}, {"asm", TokenIdKeywordAsm}, {"async", TokenIdKeywordAsync}, {"await", TokenIdKeywordAwait}, @@ -1496,6 +1502,12 @@ const char * token_name(TokenId id) { case TokenIdIntLiteral: return "IntLiteral"; case TokenIdKeywordAsync: return "async"; case TokenIdKeywordAnyerror: return "anyerror"; + case TokenIdKeywordArmInterruptCC: return "arminterruptcc"; + case TokenIdKeywordArmIrqCC: return "armirqcc"; + case TokenIdKeywordArmFiqCC: return "armfiqcc"; + case TokenIdKeywordArmSwiCC: return "armswicc"; + case TokenIdKeywordArmAbortCC: return "armabortcc"; + case TokenIdKeywordArmUndefCC: return "armundefcc"; case TokenIdKeywordAllowZero: return "allowzero"; case TokenIdKeywordAwait: return "await"; case TokenIdKeywordResume: return "resume"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index f898ca4e5949..6c8edc20bb5a 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -53,6 +53,12 @@ enum TokenId { TokenIdKeywordAllowZero, TokenIdKeywordAnd, TokenIdKeywordAnyerror, + TokenIdKeywordArmInterruptCC, + TokenIdKeywordArmIrqCC, + TokenIdKeywordArmFiqCC, + TokenIdKeywordArmSwiCC, + TokenIdKeywordArmAbortCC, + TokenIdKeywordArmUndefCC, TokenIdKeywordAsm, TokenIdKeywordAsync, TokenIdKeywordAwait,