mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
add inline assembly support
This commit is contained in:
parent
3e8a98fa61
commit
0dbee2300e
@ -115,7 +115,12 @@ set(C_HEADERS
|
|||||||
"${CMAKE_SOURCE_DIR}/c_headers/xtestintrin.h"
|
"${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(C_HEADERS_DEST "lib/zig/include")
|
||||||
|
set(ZIG_STD_DEST "lib/zig/std")
|
||||||
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
|
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
|
||||||
configure_file (
|
configure_file (
|
||||||
"${CMAKE_SOURCE_DIR}/src/config.h.in"
|
"${CMAKE_SOURCE_DIR}/src/config.h.in"
|
||||||
@ -142,6 +147,7 @@ target_link_libraries(zig LINK_PUBLIC
|
|||||||
install(TARGETS zig DESTINATION bin)
|
install(TARGETS zig DESTINATION bin)
|
||||||
|
|
||||||
install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST})
|
install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST})
|
||||||
|
install(FILES ${ZIG_STD_SRC} DESTINATION ${ZIG_STD_DEST})
|
||||||
|
|
||||||
add_executable(run_tests ${TEST_SOURCES})
|
add_executable(run_tests ${TEST_SOURCES})
|
||||||
target_link_libraries(run_tests)
|
target_link_libraries(run_tests)
|
||||||
|
|||||||
16
README.md
16
README.md
@ -58,7 +58,6 @@ compromises backward compatibility.
|
|||||||
* structs
|
* structs
|
||||||
* loops
|
* loops
|
||||||
* enums
|
* enums
|
||||||
* inline assembly and syscalls
|
|
||||||
* conditional compilation and ability to check target platform and architecture
|
* conditional compilation and ability to check target platform and architecture
|
||||||
* main function with command line arguments
|
* main function with command line arguments
|
||||||
* void pointer constant
|
* void pointer constant
|
||||||
@ -83,10 +82,23 @@ compromises backward compatibility.
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
### Debug / Development Build
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd)
|
||||||
make
|
make
|
||||||
|
make install
|
||||||
./run_tests
|
./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
|
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
|
AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,8 @@ if exists("b:current_syntax")
|
|||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
|
|
||||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as use while
|
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
|
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 zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128
|
||||||
|
|
||||||
syn keyword zigConstant null
|
syn keyword zigConstant null
|
||||||
|
|||||||
11
example/hello_world/hello2.zig
Normal file
11
example/hello_world/hello2.zig
Normal file
@ -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 NodeTypeIfExpr:
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
|
case NodeTypeAsmExpr:
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
zig_panic("unreachable");
|
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) {
|
for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) {
|
||||||
AstNode *directive_node = node->data.fn_proto.directives->at(i);
|
AstNode *directive_node = node->data.fn_proto.directives->at(i);
|
||||||
Buf *name = &directive_node->data.directive.name;
|
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) {
|
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);
|
resolve_function_proto(g, proto_node, fn_table_entry);
|
||||||
|
|
||||||
|
|
||||||
assert(!proto_node->codegen_node);
|
assert(!proto_node->codegen_node);
|
||||||
proto_node->codegen_node = allocate<CodeGenNode>(1);
|
proto_node->codegen_node = allocate<CodeGenNode>(1);
|
||||||
proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
|
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 NodeTypeIfExpr:
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
|
case NodeTypeAsmExpr:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -626,6 +647,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
|||||||
return_type = g->builtin_types.entry_unreachable;
|
return_type = g->builtin_types.entry_unreachable;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NodeTypeAsmExpr:
|
||||||
|
{
|
||||||
|
return_type = g->builtin_types.entry_void;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case NodeTypeBinOpExpr:
|
case NodeTypeBinOpExpr:
|
||||||
{
|
{
|
||||||
switch (node->data.bin_op_expr.bin_op) {
|
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 NodeTypeIfExpr:
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
|
case NodeTypeAsmExpr:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,6 +82,11 @@ struct LabelTableEntry {
|
|||||||
bool entered_from_fallthrough;
|
bool entered_from_fallthrough;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum FnAttrId {
|
||||||
|
FnAttrIdNaked,
|
||||||
|
FnAttrIdAlwaysInline,
|
||||||
|
};
|
||||||
|
|
||||||
struct FnTableEntry {
|
struct FnTableEntry {
|
||||||
LLVMValueRef fn_value;
|
LLVMValueRef fn_value;
|
||||||
AstNode *proto_node;
|
AstNode *proto_node;
|
||||||
@ -90,6 +95,7 @@ struct FnTableEntry {
|
|||||||
bool internal_linkage;
|
bool internal_linkage;
|
||||||
unsigned calling_convention;
|
unsigned calling_convention;
|
||||||
ImportTableEntry *import_entry;
|
ImportTableEntry *import_entry;
|
||||||
|
ZigList<FnAttrId> fn_attr_list;
|
||||||
|
|
||||||
// reminder: hash tables must be initialized before use
|
// reminder: hash tables must be initialized before use
|
||||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
||||||
@ -113,6 +119,7 @@ struct CodeGen {
|
|||||||
TypeTableEntry *entry_bool;
|
TypeTableEntry *entry_bool;
|
||||||
TypeTableEntry *entry_u8;
|
TypeTableEntry *entry_u8;
|
||||||
TypeTableEntry *entry_i32;
|
TypeTableEntry *entry_i32;
|
||||||
|
TypeTableEntry *entry_isize;
|
||||||
TypeTableEntry *entry_f32;
|
TypeTableEntry *entry_f32;
|
||||||
TypeTableEntry *entry_string_literal;
|
TypeTableEntry *entry_string_literal;
|
||||||
TypeTableEntry *entry_void;
|
TypeTableEntry *entry_void;
|
||||||
@ -124,6 +131,7 @@ struct CodeGen {
|
|||||||
unsigned pointer_size_bytes;
|
unsigned pointer_size_bytes;
|
||||||
bool is_static;
|
bool is_static;
|
||||||
bool strip_debug_symbols;
|
bool strip_debug_symbols;
|
||||||
|
bool insert_bootstrap_code;
|
||||||
CodeGenBuildType build_type;
|
CodeGenBuildType build_type;
|
||||||
LLVMTargetMachineRef target_machine;
|
LLVMTargetMachineRef target_machine;
|
||||||
bool is_native_target;
|
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;
|
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) {
|
static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case NodeTypeBinOpExpr:
|
case NodeTypeBinOpExpr:
|
||||||
@ -663,6 +698,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
|||||||
return LLVMConstNull(LLVMInt1Type());
|
return LLVMConstNull(LLVMInt1Type());
|
||||||
case NodeTypeIfExpr:
|
case NodeTypeIfExpr:
|
||||||
return gen_if_expr(g, node);
|
return gen_if_expr(g, node);
|
||||||
|
case NodeTypeAsmExpr:
|
||||||
|
return gen_asm_expr(g, node);
|
||||||
case NodeTypeNumberLiteral:
|
case NodeTypeNumberLiteral:
|
||||||
{
|
{
|
||||||
Buf *number_str = &node->data.number;
|
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);
|
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) {
|
static void do_code_gen(CodeGen *g) {
|
||||||
assert(!g->errors.length);
|
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);
|
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);
|
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);
|
LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage);
|
||||||
|
|
||||||
if (type_is_unreachable(g, fn_proto->return_type)) {
|
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->type_table.put(&entry->name, entry);
|
||||||
g->builtin_types.entry_i32 = 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);
|
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
|
||||||
entry->type_ref = LLVMFloatType();
|
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);
|
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) {
|
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);
|
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 (top_level_decl->type == NodeTypeUse) {
|
||||||
if (!entry) {
|
auto entry = g->import_table.maybe_get(&top_level_decl->data.use.path);
|
||||||
Buf full_path = BUF_INIT;
|
if (!entry) {
|
||||||
os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path);
|
Buf full_path = BUF_INIT;
|
||||||
Buf *import_code = buf_alloc();
|
os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path);
|
||||||
if ((err = os_fetch_file_path(&full_path, import_code))) {
|
Buf *import_code = buf_alloc();
|
||||||
add_node_error(g, top_level_decl,
|
if ((err = os_fetch_file_path(&full_path, import_code))) {
|
||||||
buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
|
add_node_error(g, top_level_decl,
|
||||||
break;
|
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);
|
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) {
|
if (g->verbose) {
|
||||||
fprintf(stderr, "\nSemantic Analysis:\n");
|
fprintf(stderr, "\nSemantic Analysis:\n");
|
||||||
fprintf(stderr, "--------------------\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) {
|
} else if (type_entry == g->builtin_types.entry_i32) {
|
||||||
g->c_stdint_used = true;
|
g->c_stdint_used = true;
|
||||||
buf_init_from_str(out_buf, "int32_t");
|
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) {
|
} else if (type_entry == g->builtin_types.entry_f32) {
|
||||||
buf_init_from_str(out_buf, "float");
|
buf_init_from_str(out_buf, "float");
|
||||||
} else if (type_entry == g->builtin_types.entry_unreachable) {
|
} else if (type_entry == g->builtin_types.entry_unreachable) {
|
||||||
|
|||||||
@ -7,5 +7,6 @@
|
|||||||
#define ZIG_VERSION_STRING "@ZIG_VERSION@"
|
#define ZIG_VERSION_STRING "@ZIG_VERSION@"
|
||||||
|
|
||||||
#define ZIG_HEADERS_DIR "@CMAKE_INSTALL_PREFIX@/@C_HEADERS_DEST@"
|
#define ZIG_HEADERS_DIR "@CMAKE_INSTALL_PREFIX@/@C_HEADERS_DEST@"
|
||||||
|
#define ZIG_STD_DIR "@CMAKE_INSTALL_PREFIX@/@ZIG_STD_DEST@"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
119
src/parser.cpp
119
src/parser.cpp
@ -104,6 +104,8 @@ const char *node_type_str(NodeType node_type) {
|
|||||||
return "Label";
|
return "Label";
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
return "Label";
|
return "Label";
|
||||||
|
case NodeTypeAsmExpr:
|
||||||
|
return "AsmExpr";
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -290,6 +292,9 @@ void ast_print(AstNode *node, int indent) {
|
|||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name));
|
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name));
|
||||||
break;
|
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);
|
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) {
|
static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf) {
|
||||||
// skip the double quotes at beginning and end
|
// skip the double quotes at beginning and end
|
||||||
// convert escape sequences
|
// 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) {
|
static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
Token *token = &pc->tokens->at(*token_index);
|
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)
|
if (ass_expr)
|
||||||
return ass_expr;
|
return ass_expr;
|
||||||
|
|
||||||
|
AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false);
|
||||||
|
if (asm_expr)
|
||||||
|
return asm_expr;
|
||||||
|
|
||||||
if (mandatory)
|
if (mandatory)
|
||||||
ast_invalid_token_error(pc, token);
|
ast_invalid_token_error(pc, token);
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
struct AstNode;
|
struct AstNode;
|
||||||
struct CodeGenNode;
|
struct CodeGenNode;
|
||||||
struct ImportTableEntry;
|
struct ImportTableEntry;
|
||||||
|
struct AsmToken;
|
||||||
|
|
||||||
enum NodeType {
|
enum NodeType {
|
||||||
NodeTypeRoot,
|
NodeTypeRoot,
|
||||||
@ -45,6 +46,7 @@ enum NodeType {
|
|||||||
NodeTypeIfExpr,
|
NodeTypeIfExpr,
|
||||||
NodeTypeLabel,
|
NodeTypeLabel,
|
||||||
NodeTypeGoto,
|
NodeTypeGoto,
|
||||||
|
NodeTypeAsmExpr,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstNodeRoot {
|
struct AstNodeRoot {
|
||||||
@ -203,6 +205,12 @@ struct AstNodeGoto {
|
|||||||
Buf name;
|
Buf name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AstNodeAsmExpr {
|
||||||
|
bool is_volatile;
|
||||||
|
Buf asm_template;
|
||||||
|
ZigList<AsmToken> token_list;
|
||||||
|
};
|
||||||
|
|
||||||
struct AstNode {
|
struct AstNode {
|
||||||
enum NodeType type;
|
enum NodeType type;
|
||||||
int line;
|
int line;
|
||||||
@ -231,6 +239,7 @@ struct AstNode {
|
|||||||
AstNodeIfExpr if_expr;
|
AstNodeIfExpr if_expr;
|
||||||
AstNodeLabel label;
|
AstNodeLabel label;
|
||||||
AstNodeGoto go_to;
|
AstNodeGoto go_to;
|
||||||
|
AstNodeAsmExpr asm_expr;
|
||||||
Buf number;
|
Buf number;
|
||||||
Buf string;
|
Buf string;
|
||||||
Buf symbol;
|
Buf symbol;
|
||||||
@ -238,6 +247,17 @@ struct AstNode {
|
|||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AsmTokenId {
|
||||||
|
AsmTokenIdTemplate,
|
||||||
|
AsmTokenIdPercent,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AsmToken {
|
||||||
|
enum AsmTokenId id;
|
||||||
|
int start;
|
||||||
|
int end;
|
||||||
|
};
|
||||||
|
|
||||||
__attribute__ ((format (printf, 2, 3)))
|
__attribute__ ((format (printf, 2, 3)))
|
||||||
void ast_token_error(Token *token, const char *format, ...);
|
void ast_token_error(Token *token, const char *format, ...);
|
||||||
|
|
||||||
|
|||||||
@ -197,6 +197,10 @@ static void end_token(Tokenize *t) {
|
|||||||
t->cur_tok->id = TokenIdKeywordElse;
|
t->cur_tok->id = TokenIdKeywordElse;
|
||||||
} else if (mem_eql_str(token_mem, token_len, "goto")) {
|
} else if (mem_eql_str(token_mem, token_len, "goto")) {
|
||||||
t->cur_tok->id = TokenIdKeywordGoto;
|
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;
|
t->cur_tok = nullptr;
|
||||||
@ -637,6 +641,8 @@ static const char * token_name(Token *token) {
|
|||||||
case TokenIdKeywordIf: return "If";
|
case TokenIdKeywordIf: return "If";
|
||||||
case TokenIdKeywordElse: return "Else";
|
case TokenIdKeywordElse: return "Else";
|
||||||
case TokenIdKeywordGoto: return "Goto";
|
case TokenIdKeywordGoto: return "Goto";
|
||||||
|
case TokenIdKeywordVolatile: return "Volatile";
|
||||||
|
case TokenIdKeywordAsm: return "Asm";
|
||||||
case TokenIdLParen: return "LParen";
|
case TokenIdLParen: return "LParen";
|
||||||
case TokenIdRParen: return "RParen";
|
case TokenIdRParen: return "RParen";
|
||||||
case TokenIdComma: return "Comma";
|
case TokenIdComma: return "Comma";
|
||||||
@ -687,3 +693,40 @@ void print_tokens(Buf *buf, ZigList<Token> *tokens) {
|
|||||||
fprintf(stderr, "\n");
|
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,
|
TokenIdKeywordIf,
|
||||||
TokenIdKeywordElse,
|
TokenIdKeywordElse,
|
||||||
TokenIdKeywordGoto,
|
TokenIdKeywordGoto,
|
||||||
|
TokenIdKeywordAsm,
|
||||||
|
TokenIdKeywordVolatile,
|
||||||
TokenIdLParen,
|
TokenIdLParen,
|
||||||
TokenIdRParen,
|
TokenIdRParen,
|
||||||
TokenIdComma,
|
TokenIdComma,
|
||||||
@ -90,4 +92,6 @@ void tokenize(Buf *buf, Tokenization *out_tokenization);
|
|||||||
|
|
||||||
void print_tokens(Buf *buf, ZigList<Token> *tokens);
|
void print_tokens(Buf *buf, ZigList<Token> *tokens);
|
||||||
|
|
||||||
|
bool is_printable(uint8_t c);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
16
std/bootstrap.zig
Normal file
16
std/bootstrap.zig
Normal file
@ -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);
|
exit(0);
|
||||||
}
|
}
|
||||||
)SOURCE", "OK\n");
|
)SOURCE", "OK\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_compile_failure_test_cases(void) {
|
static void add_compile_failure_test_cases(void) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user