add inline assembly support
parent
3e8a98fa61
commit
0dbee2300e
|
@ -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)
|
||||
|
|
16
README.md
16
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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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<CodeGenNode>(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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<FnAttrId> fn_attr_list;
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> 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;
|
||||
|
|
113
src/codegen.cpp
113
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) {
|
||||
|
|
|
@ -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
|
||||
|
|
119
src/parser.cpp
119
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<AsmToken> *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);
|
||||
|
||||
|
|
|
@ -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<AsmToken> 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, ...);
|
||||
|
||||
|
|
|
@ -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<Token> *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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Token> *tokens);
|
||||
|
||||
bool is_printable(uint8_t c);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -397,6 +397,7 @@ loop_2_end:
|
|||
exit(0);
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
}
|
||||
|
||||
static void add_compile_failure_test_cases(void) {
|
||||
|
|
Loading…
Reference in New Issue