diff --git a/CMakeLists.txt b/CMakeLists.txt index 3321ea5151..ffdc252b0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,7 +115,12 @@ set(C_HEADERS "${CMAKE_SOURCE_DIR}/c_headers/xtestintrin.h" ) +set(ZIG_STD_SRC + "${CMAKE_SOURCE_DIR}/std/bootstrap.zig" +) + set(C_HEADERS_DEST "lib/zig/include") +set(ZIG_STD_DEST "lib/zig/std") set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h") configure_file ( "${CMAKE_SOURCE_DIR}/src/config.h.in" @@ -142,6 +147,7 @@ target_link_libraries(zig LINK_PUBLIC install(TARGETS zig DESTINATION bin) install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST}) +install(FILES ${ZIG_STD_SRC} DESTINATION ${ZIG_STD_DEST}) add_executable(run_tests ${TEST_SOURCES}) target_link_libraries(run_tests) diff --git a/README.md b/README.md index 3423251a11..2d59372970 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,6 @@ compromises backward compatibility. * structs * loops * enums - * inline assembly and syscalls * conditional compilation and ability to check target platform and architecture * main function with command line arguments * void pointer constant @@ -83,10 +82,23 @@ compromises backward compatibility. ## Building +### Debug / Development Build + ``` mkdir build cd build -cmake .. +cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) make +make install ./run_tests ``` + +### Release / Install Build + +``` +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release +make +sudo make install +``` diff --git a/doc/langref.md b/doc/langref.md index 0e1efdccad..6843ce8a35 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -70,7 +70,15 @@ VariableDeclaration : token(Let) option(token(Mut)) token(Symbol) (token(Eq) Exp Expression : BlockExpression | NonBlockExpression -NonBlockExpression : ReturnExpression | AssignmentExpression +NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression + +AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) + +AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) + +AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) + +AsmClobbers: token(Colon) list(token(String), token(Comma)) AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index 132ed02162..bc63e343b6 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -7,8 +7,8 @@ if exists("b:current_syntax") finish endif -syn keyword zigKeyword fn return mut const extern unreachable export pub as use while -syn keyword zigKeyword if else let void goto type enum struct continue break match +syn keyword zigKeyword fn return mut const extern unreachable export pub as use while asm +syn keyword zigKeyword if else let void goto type enum struct continue break match volatile syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 syn keyword zigConstant null diff --git a/example/hello_world/hello2.zig b/example/hello_world/hello2.zig new file mode 100644 index 0000000000..b31d0542bc --- /dev/null +++ b/example/hello_world/hello2.zig @@ -0,0 +1,11 @@ +export executable "hello"; + +#link("c") +extern { + fn printf(__format: *const u8, ...) -> i32; +} + +export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { + printf("argc = %zu\n", argc); + return 0; +} diff --git a/src/analyze.cpp b/src/analyze.cpp index e4c3b2ca7b..8b61bec7b0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -43,6 +43,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeIfExpr: case NodeTypeLabel: case NodeTypeGoto: + case NodeTypeAsmExpr: return node; } zig_panic("unreachable"); @@ -205,8 +206,26 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) { AstNode *directive_node = node->data.fn_proto.directives->at(i); Buf *name = &directive_node->data.directive.name; - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); + + if (buf_eql_str(name, "attribute")) { + Buf *attr_name = &directive_node->data.directive.param; + if (fn_table_entry->fn_def_node) { + if (buf_eql_str(attr_name, "naked")) { + fn_table_entry->fn_attr_list.append(FnAttrIdNaked); + } else if (buf_eql_str(attr_name, "alwaysinline")) { + fn_table_entry->fn_attr_list.append(FnAttrIdAlwaysInline); + } else { + add_node_error(g, directive_node, + buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); + } + } else { + add_node_error(g, directive_node, + buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); + } + } else { + add_node_error(g, directive_node, + buf_sprintf("invalid directive: '%s'", buf_ptr(name))); + } } for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { @@ -338,6 +357,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, resolve_function_proto(g, proto_node, fn_table_entry); + assert(!proto_node->codegen_node); proto_node->codegen_node = allocate(1); proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; @@ -415,6 +435,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, case NodeTypeIfExpr: case NodeTypeLabel: case NodeTypeGoto: + case NodeTypeAsmExpr: zig_unreachable(); } } @@ -626,6 +647,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, return_type = g->builtin_types.entry_unreachable; break; } + case NodeTypeAsmExpr: + { + return_type = g->builtin_types.entry_void; + break; + } case NodeTypeBinOpExpr: { switch (node->data.bin_op_expr.bin_op) { @@ -1004,6 +1030,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, case NodeTypeIfExpr: case NodeTypeLabel: case NodeTypeGoto: + case NodeTypeAsmExpr: zig_unreachable(); } } diff --git a/src/analyze.hpp b/src/analyze.hpp index dca9f37cbf..cf719edbe5 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -82,6 +82,11 @@ struct LabelTableEntry { bool entered_from_fallthrough; }; +enum FnAttrId { + FnAttrIdNaked, + FnAttrIdAlwaysInline, +}; + struct FnTableEntry { LLVMValueRef fn_value; AstNode *proto_node; @@ -90,6 +95,7 @@ struct FnTableEntry { bool internal_linkage; unsigned calling_convention; ImportTableEntry *import_entry; + ZigList fn_attr_list; // reminder: hash tables must be initialized before use HashMap label_table; @@ -113,6 +119,7 @@ struct CodeGen { TypeTableEntry *entry_bool; TypeTableEntry *entry_u8; TypeTableEntry *entry_i32; + TypeTableEntry *entry_isize; TypeTableEntry *entry_f32; TypeTableEntry *entry_string_literal; TypeTableEntry *entry_void; @@ -124,6 +131,7 @@ struct CodeGen { unsigned pointer_size_bytes; bool is_static; bool strip_debug_symbols; + bool insert_bootstrap_code; CodeGenBuildType build_type; LLVMTargetMachineRef target_machine; bool is_native_target; diff --git a/src/codegen.cpp b/src/codegen.cpp index bc8ece3fe8..5a177792b4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -609,6 +609,41 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i return return_value; } +static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeAsmExpr); + + Buf *src_template = &node->data.asm_expr.asm_template; + + Buf llvm_template = BUF_INIT; + buf_resize(&llvm_template, 0); + + for (int token_i = 0; token_i < node->data.asm_expr.token_list.length; token_i += 1) { + AsmToken *asm_token = &node->data.asm_expr.token_list.at(token_i); + switch (asm_token->id) { + case AsmTokenIdTemplate: + for (int offset = asm_token->start; offset < asm_token->end; offset += 1) { + uint8_t c = *((uint8_t*)(buf_ptr(src_template) + offset)); + if (c == '$') { + buf_append_str(&llvm_template, "$$"); + } else { + buf_append_char(&llvm_template, c); + } + } + break; + case AsmTokenIdPercent: + buf_append_char(&llvm_template, '%'); + break; + } + } + + LLVMTypeRef function_type = LLVMFunctionType(LLVMVoidType(), nullptr, 0, false); + + LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), "", true, false); + + add_debug_source_node(g, node); + return LLVMBuildCall(g->builder, asm_fn, nullptr, 0, ""); +} + static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { switch (node->type) { case NodeTypeBinOpExpr: @@ -663,6 +698,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { return LLVMConstNull(LLVMInt1Type()); case NodeTypeIfExpr: return gen_if_expr(g, node); + case NodeTypeAsmExpr: + return gen_asm_expr(g, node); case NodeTypeNumberLiteral: { Buf *number_str = &node->data.number; @@ -762,6 +799,16 @@ static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnPro return LLVMZigCreateSubroutineType(g->dbuilder, di_file, types, types_len, 0); } +static LLVMAttribute to_llvm_fn_attr(FnAttrId attr_id) { + switch (attr_id) { + case FnAttrIdNaked: + return LLVMNakedAttribute; + case FnAttrIdAlwaysInline: + return LLVMAlwaysInlineAttribute; + } + zig_unreachable(); +} + static void do_code_gen(CodeGen *g) { assert(!g->errors.length); @@ -789,6 +836,11 @@ static void do_code_gen(CodeGen *g) { LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, fn_proto->is_var_args); LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type); + for (int attr_i = 0; attr_i < fn_table_entry->fn_attr_list.length; attr_i += 1) { + FnAttrId attr_id = fn_table_entry->fn_attr_list.at(attr_i); + LLVMAddFunctionAttr(fn, to_llvm_fn_attr(attr_id)); + } + LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage); if (type_is_unreachable(g, fn_proto->return_type)) { @@ -966,6 +1018,19 @@ static void define_primitive_types(CodeGen *g) { g->type_table.put(&entry->name, entry); g->builtin_types.entry_i32 = entry; } + { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); + entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); + buf_init_from_str(&entry->name, "isize"); + entry->size_in_bits = g->pointer_size_bytes * 8; + entry->align_in_bits = g->pointer_size_bytes * 8; + entry->data.integral.is_signed = true; + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + entry->size_in_bits, entry->align_in_bits, + LLVMZigEncoding_DW_ATE_signed()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_isize = entry; + } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); entry->type_ref = LLVMFloatType(); @@ -1121,20 +1186,30 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou assert(import_entry->root->type == NodeTypeRoot); for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) { AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i); - if (top_level_decl->type != NodeTypeUse) - continue; - auto entry = g->import_table.maybe_get(&top_level_decl->data.use.path); - if (!entry) { - Buf full_path = BUF_INIT; - os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); - Buf *import_code = buf_alloc(); - if ((err = os_fetch_file_path(&full_path, import_code))) { - add_node_error(g, top_level_decl, - buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); - break; + if (top_level_decl->type == NodeTypeUse) { + auto entry = g->import_table.maybe_get(&top_level_decl->data.use.path); + if (!entry) { + Buf full_path = BUF_INIT; + os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); + Buf *import_code = buf_alloc(); + if ((err = os_fetch_file_path(&full_path, import_code))) { + add_node_error(g, top_level_decl, + buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); + break; + } + codegen_add_code(g, &top_level_decl->data.use.path, import_code); + } + } else if (top_level_decl->type == NodeTypeFnDef) { + AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; + assert(proto_node->type == NodeTypeFnProto); + Buf *proto_name = &proto_node->data.fn_proto.name; + + bool is_exported = (proto_node->data.fn_proto.visib_mod == FnProtoVisibModExport); + + if (buf_eql_str(proto_name, "main") && is_exported) { + g->insert_bootstrap_code = true; } - codegen_add_code(g, &top_level_decl->data.use.path, import_code); } } @@ -1146,6 +1221,17 @@ void codegen_add_root_code(CodeGen *g, Buf *source_path, Buf *source_code) { g->root_import = codegen_add_code(g, source_path, source_code); + if (g->insert_bootstrap_code) { + Buf *path_to_bootstrap_src = buf_sprintf("%s/bootstrap.zig", ZIG_STD_DIR); + Buf *import_code = buf_alloc(); + int err; + if ((err = os_fetch_file_path(path_to_bootstrap_src, import_code))) { + zig_panic("unable to open '%s': %s", buf_ptr(path_to_bootstrap_src), err_str(err)); + } + + codegen_add_code(g, path_to_bootstrap_src, import_code); + } + if (g->verbose) { fprintf(stderr, "\nSemantic Analysis:\n"); fprintf(stderr, "--------------------\n"); @@ -1185,6 +1271,9 @@ static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) { } else if (type_entry == g->builtin_types.entry_i32) { g->c_stdint_used = true; buf_init_from_str(out_buf, "int32_t"); + } else if (type_entry == g->builtin_types.entry_isize) { + g->c_stdint_used = true; + buf_init_from_str(out_buf, "intptr_t"); } else if (type_entry == g->builtin_types.entry_f32) { buf_init_from_str(out_buf, "float"); } else if (type_entry == g->builtin_types.entry_unreachable) { diff --git a/src/config.h.in b/src/config.h.in index d4ae8a74e6..a3a31d31b2 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -7,5 +7,6 @@ #define ZIG_VERSION_STRING "@ZIG_VERSION@" #define ZIG_HEADERS_DIR "@CMAKE_INSTALL_PREFIX@/@C_HEADERS_DEST@" +#define ZIG_STD_DIR "@CMAKE_INSTALL_PREFIX@/@ZIG_STD_DEST@" #endif diff --git a/src/parser.cpp b/src/parser.cpp index 6c8a54ad9e..6740a065a2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -104,6 +104,8 @@ const char *node_type_str(NodeType node_type) { return "Label"; case NodeTypeGoto: return "Label"; + case NodeTypeAsmExpr: + return "AsmExpr"; } zig_unreachable(); } @@ -290,6 +292,9 @@ void ast_print(AstNode *node, int indent) { case NodeTypeGoto: fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); break; + case NodeTypeAsmExpr: + fprintf(stderr, "%s\n", node_type_str(node->type)); + break; } } @@ -360,6 +365,71 @@ static void ast_buf_from_token(ParseContext *pc, Token *token, Buf *buf) { buf_init_from_mem(buf, buf_ptr(pc->buf) + token->start_pos, token->end_pos - token->start_pos); } +static void parse_asm_template(ParseContext *pc, AstNode *node) { + Buf *asm_template = &node->data.asm_expr.asm_template; + + enum State { + StateStart, + StatePercent, + StateTemplate, + }; + + ZigList *tok_list = &node->data.asm_expr.token_list; + assert(tok_list->length == 0); + + AsmToken *cur_tok = nullptr; + + enum State state = StateStart; + + for (int i = 0; i < buf_len(asm_template); i += 1) { + uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i); + switch (state) { + case StateStart: + if (c == '%') { + tok_list->add_one(); + cur_tok = &tok_list->last(); + cur_tok->id = AsmTokenIdPercent; + cur_tok->start = i; + state = StatePercent; + } else { + tok_list->add_one(); + cur_tok = &tok_list->last(); + cur_tok->id = AsmTokenIdTemplate; + cur_tok->start = i; + state = StateTemplate; + } + break; + case StatePercent: + if (c == '%') { + cur_tok->end = i; + state = StateStart; + } else { + zig_panic("TODO handle assembly tokenize error"); + } + break; + case StateTemplate: + if (c == '%') { + cur_tok->end = i; + i -= 1; + cur_tok = nullptr; + state = StateStart; + } + break; + } + } + + switch (state) { + case StateStart: + break; + case StatePercent: + zig_panic("TODO handle assembly tokenize error eof"); + break; + case StateTemplate: + cur_tok->end = buf_len(asm_template); + break; + } +} + static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf) { // skip the double quotes at beginning and end // convert escape sequences @@ -1264,7 +1334,50 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand } /* -NonBlockExpression : ReturnExpression | AssignmentExpression +AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) +*/ +static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mandatory) { + Token *asm_token = &pc->tokens->at(*token_index); + + if (asm_token->id != TokenIdKeywordAsm) { + if (mandatory) { + ast_invalid_token_error(pc, asm_token); + } else { + return nullptr; + } + } + + AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token); + + *token_index += 1; + Token *lparen_tok = &pc->tokens->at(*token_index); + + if (lparen_tok->id == TokenIdKeywordVolatile) { + node->data.asm_expr.is_volatile = true; + + *token_index += 1; + lparen_tok = &pc->tokens->at(*token_index); + } + + ast_expect_token(pc, lparen_tok, TokenIdLParen); + *token_index += 1; + + Token *template_tok = &pc->tokens->at(*token_index); + ast_expect_token(pc, template_tok, TokenIdStringLiteral); + *token_index += 1; + + parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template); + parse_asm_template(pc, node); + + Token *rparen_tok = &pc->tokens->at(*token_index); + ast_expect_token(pc, rparen_tok, TokenIdRParen); + *token_index += 1; + + return node; +} + +/* +NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression */ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1277,6 +1390,10 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo if (ass_expr) return ass_expr; + AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false); + if (asm_expr) + return asm_expr; + if (mandatory) ast_invalid_token_error(pc, token); diff --git a/src/parser.hpp b/src/parser.hpp index 4dfac7472c..f8b213f991 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -16,6 +16,7 @@ struct AstNode; struct CodeGenNode; struct ImportTableEntry; +struct AsmToken; enum NodeType { NodeTypeRoot, @@ -45,6 +46,7 @@ enum NodeType { NodeTypeIfExpr, NodeTypeLabel, NodeTypeGoto, + NodeTypeAsmExpr, }; struct AstNodeRoot { @@ -203,6 +205,12 @@ struct AstNodeGoto { Buf name; }; +struct AstNodeAsmExpr { + bool is_volatile; + Buf asm_template; + ZigList token_list; +}; + struct AstNode { enum NodeType type; int line; @@ -231,6 +239,7 @@ struct AstNode { AstNodeIfExpr if_expr; AstNodeLabel label; AstNodeGoto go_to; + AstNodeAsmExpr asm_expr; Buf number; Buf string; Buf symbol; @@ -238,6 +247,17 @@ struct AstNode { } data; }; +enum AsmTokenId { + AsmTokenIdTemplate, + AsmTokenIdPercent, +}; + +struct AsmToken { + enum AsmTokenId id; + int start; + int end; +}; + __attribute__ ((format (printf, 2, 3))) void ast_token_error(Token *token, const char *format, ...); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 2419bfc3ad..8e6d6c3082 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -197,6 +197,10 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordElse; } else if (mem_eql_str(token_mem, token_len, "goto")) { t->cur_tok->id = TokenIdKeywordGoto; + } else if (mem_eql_str(token_mem, token_len, "volatile")) { + t->cur_tok->id = TokenIdKeywordVolatile; + } else if (mem_eql_str(token_mem, token_len, "asm")) { + t->cur_tok->id = TokenIdKeywordAsm; } t->cur_tok = nullptr; @@ -637,6 +641,8 @@ static const char * token_name(Token *token) { case TokenIdKeywordIf: return "If"; case TokenIdKeywordElse: return "Else"; case TokenIdKeywordGoto: return "Goto"; + case TokenIdKeywordVolatile: return "Volatile"; + case TokenIdKeywordAsm: return "Asm"; case TokenIdLParen: return "LParen"; case TokenIdRParen: return "RParen"; case TokenIdComma: return "Comma"; @@ -687,3 +693,40 @@ void print_tokens(Buf *buf, ZigList *tokens) { fprintf(stderr, "\n"); } } + +bool is_printable(uint8_t c) { + switch (c) { + default: + return false; + case DIGIT: + case ALPHA: + case '!': + case '#': + case '$': + case '%': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case '-': + case '.': + case '/': + case ':': + case ';': + case '<': + case '=': + case '>': + case '?': + case '@': + case '^': + case '_': + case '`': + case '~': + case ' ': + return true; + } +} + diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 56031d7184..e025f5e827 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -30,6 +30,8 @@ enum TokenId { TokenIdKeywordIf, TokenIdKeywordElse, TokenIdKeywordGoto, + TokenIdKeywordAsm, + TokenIdKeywordVolatile, TokenIdLParen, TokenIdRParen, TokenIdComma, @@ -90,4 +92,6 @@ void tokenize(Buf *buf, Tokenization *out_tokenization); void print_tokens(Buf *buf, ZigList *tokens); +bool is_printable(uint8_t c); + #endif diff --git a/std/bootstrap.zig b/std/bootstrap.zig new file mode 100644 index 0000000000..348b74a27e --- /dev/null +++ b/std/bootstrap.zig @@ -0,0 +1,16 @@ + +// TODO conditionally compile this differently for non-ELF +#attribute("naked") +export fn _start() -> unreachable { + // TODO conditionally compile this differently for other architectures and other OSes + asm volatile (" + mov (%%rsp), %%rdi // first parameter is argc + lea 0x8(%%rsp), %%rsi // second parameter is argv + lea 0x10(%%rsp,%%rdi,8), %%rdx // third paremeter is env + callq main + mov %%rax, %%rdi // return value is the parameter to exit syscall + mov $60, %%rax // 60 is exit syscall number + syscall + "); + unreachable +} diff --git a/test/run_tests.cpp b/test/run_tests.cpp index d3bc5f8bde..2ea6befd0d 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -397,6 +397,7 @@ loop_2_end: exit(0); } )SOURCE", "OK\n"); + } static void add_compile_failure_test_cases(void) {