diff --git a/doc/langref.md b/doc/langref.md index 6843ce8a35..8ab24122b7 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -78,6 +78,10 @@ AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) +AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) token(Symbol) token(RParen) + +AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) + AsmClobbers: token(Colon) list(token(String), token(Comma)) AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression diff --git a/example/hello_world/hello2.zig b/example/hello_world/hello2.zig index 45756a3b1d..d480c3aeec 100644 --- a/example/hello_world/hello2.zig +++ b/example/hello_world/hello2.zig @@ -3,6 +3,6 @@ export executable "hello"; use "std.zig"; export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { - print_str("Hello, world!", 13); + print_str("Hello, world!", 13 as isize); return 0; } diff --git a/src/analyze.cpp b/src/analyze.cpp index 8b61bec7b0..a2b4801144 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -536,6 +536,20 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i return return_type; } +static TypeTableEntry *analyze_variable_name(CodeGen *g, BlockContext *context, + AstNode *node, Buf *variable_name) +{ + LocalVariableTableEntry *local_variable = find_local_variable(context, variable_name); + if (local_variable) { + return local_variable->type; + } else { + // TODO: check global variables also + add_node_error(g, node, + buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); + return g->builtin_types.entry_invalid; + } +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -649,6 +663,15 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, } case NodeTypeAsmExpr: { + for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + analyze_variable_name(g, context, node, &asm_output->variable_name); + } + for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + analyze_expression(g, import, context, nullptr, asm_input->expr); + } + return_type = g->builtin_types.entry_void; break; } @@ -835,22 +858,36 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeSymbol: { - Buf *symbol_name = &node->data.symbol; - LocalVariableTableEntry *local_variable = find_local_variable(context, symbol_name); - if (local_variable) { - return_type = local_variable->type; - } else { - // TODO: check global variables also - add_node_error(g, node, - buf_sprintf("use of undeclared identifier '%s'", buf_ptr(symbol_name))); - return_type = g->builtin_types.entry_invalid; - } + return_type = analyze_variable_name(g, context, node, &node->data.symbol); break; } case NodeTypeCastExpr: - zig_panic("TODO analyze_expression cast expr"); - break; + { + TypeTableEntry *wanted_type = resolve_type(g, node->data.cast_expr.type); + TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, + node->data.cast_expr.expr); + if (wanted_type->id == TypeTableEntryIdInvalid || + actual_type->id == TypeTableEntryIdInvalid) + { + return_type = g->builtin_types.entry_invalid; + break; + } + + // special casing this for now, TODO think about casting and do a general solution + if (wanted_type == g->builtin_types.entry_isize && + actual_type->id == TypeTableEntryIdPointer) + { + return_type = wanted_type; + } else if (wanted_type == g->builtin_types.entry_isize && + actual_type->id == TypeTableEntryIdInt) + { + return_type = wanted_type; + } else { + zig_panic("TODO analyze_expression cast expr"); + } + break; + } case NodeTypePrefixOpExpr: switch (node->data.prefix_op_expr.prefix_op) { case PrefixOpBoolNot: diff --git a/src/codegen.cpp b/src/codegen.cpp index b03c769a0d..80c19ed33c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -232,12 +232,35 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeCastExpr); - LLVMValueRef expr = gen_expr(g, node->data.cast_expr.prefix_op_expr); + LLVMValueRef expr_val = gen_expr(g, node->data.cast_expr.expr); - if (!node->data.cast_expr.type) - return expr; + TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr); + TypeTableEntry *wanted_type = get_expr_type(node); - zig_panic("TODO cast expression"); + // this asserts are here only because no other casting codegen is supported currently + assert(wanted_type == g->builtin_types.entry_isize); + + if (wanted_type->id == TypeTableEntryIdPointer) { + return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); + } else if (wanted_type->id == TypeTableEntryIdInt) { + if (actual_type->size_in_bits == wanted_type->size_in_bits) { + if (actual_type->id == TypeTableEntryIdPointer) { + return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); + } else { + zig_panic("TODO gen_cast_expr"); + } + } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { + if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) { + return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); + } else { + zig_panic("TODO gen_cast_expr sign mismatch"); + } + } else { + zig_panic("TODO gen_cast_expr"); + } + } else { + zig_panic("TODO gen_cast_expr"); + } } static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { @@ -610,16 +633,37 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i return return_value; } +static int find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) { + const char *ptr = buf_ptr(&node->data.asm_expr.asm_template) + tok->start + 2; + int len = tok->end - tok->start - 2; + int result = 0; + for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + if (buf_eql_mem(&asm_output->asm_symbolic_name, ptr, len)) { + return result; + } + } + for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + if (buf_eql_mem(&asm_input->asm_symbolic_name, ptr, len)) { + return result; + } + } + return -1; +} + static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeAsmExpr); - Buf *src_template = &node->data.asm_expr.asm_template; + AstNodeAsmExpr *asm_expr = &node->data.asm_expr; + + Buf *src_template = &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); + for (int token_i = 0; token_i < asm_expr->token_list.length; token_i += 1) { + AsmToken *asm_token = &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) { @@ -634,15 +678,69 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { case AsmTokenIdPercent: buf_append_char(&llvm_template, '%'); break; + case AsmTokenIdVar: + int index = find_asm_index(g, node, asm_token); + assert(index >= 0); + buf_appendf(&llvm_template, "$%d", index); + break; } } - LLVMTypeRef function_type = LLVMFunctionType(LLVMVoidType(), nullptr, 0, false); + Buf constraint_buf = BUF_INIT; + buf_resize(&constraint_buf, 0); + int total_constraint_count = asm_expr->output_list.length + + asm_expr->input_list.length + + asm_expr->clobber_list.length; + int input_and_output_count = asm_expr->output_list.length + + asm_expr->input_list.length; + int total_index = 0; + LLVMTypeRef *param_types = allocate(input_and_output_count); + LLVMValueRef *param_values = allocate(input_and_output_count); + for (int i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) { + AsmOutput *asm_output = asm_expr->output_list.at(i); + if (buf_eql_str(&asm_output->constraint, "=m")) { + buf_append_str(&constraint_buf, "=*m"); + } else { + zig_panic("TODO unable to handle anything other than '=m' for outputs"); + } + if (total_index + 1 < total_constraint_count) { + buf_append_char(&constraint_buf, ','); + } - LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), "", true, false); + LocalVariableTableEntry *variable = find_local_variable( + node->codegen_node->expr_node.block_context, + &asm_output->variable_name); + assert(variable); + param_types[total_index] = LLVMTypeOf(variable->value_ref); + param_values[total_index] = variable->value_ref; + } + for (int i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1) { + AsmInput *asm_input = asm_expr->input_list.at(i); + buf_append_buf(&constraint_buf, &asm_input->constraint); + if (total_index + 1 < total_constraint_count) { + buf_append_char(&constraint_buf, ','); + } + + TypeTableEntry *expr_type = get_expr_type(asm_input->expr); + param_types[total_index] = expr_type->type_ref; + param_values[total_index] = gen_expr(g, asm_input->expr); + } + for (int i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) { + Buf *clobber_buf = asm_expr->clobber_list.at(i); + buf_appendf(&constraint_buf, "~{%s}", buf_ptr(clobber_buf)); + if (total_index + 1 < total_constraint_count) { + buf_append_char(&constraint_buf, ','); + } + } + + LLVMTypeRef function_type = LLVMFunctionType(LLVMVoidType(), param_types, input_and_output_count, false); + + bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0); + LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), + buf_ptr(&constraint_buf), is_volatile, false); add_debug_source_node(g, node); - return LLVMBuildCall(g->builder, asm_fn, nullptr, 0, ""); + return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); } static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { diff --git a/src/parser.cpp b/src/parser.cpp index 6740a065a2..29bc069429 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -245,7 +245,7 @@ void ast_print(AstNode *node, int indent) { break; case NodeTypeCastExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); - ast_print(node->data.cast_expr.prefix_op_expr, indent + 2); + ast_print(node->data.cast_expr.expr, indent + 2); if (node->data.cast_expr.type) ast_print(node->data.cast_expr.type, indent + 2); break; @@ -307,6 +307,33 @@ struct ParseContext { ErrColor err_color; }; +__attribute__ ((format (printf, 4, 5))) +__attribute__ ((noreturn)) +static void ast_asm_error(ParseContext *pc, AstNode *node, int offset, const char *format, ...) { + assert(node->type == NodeTypeAsmExpr); + + ErrorMsg *err = allocate(1); + + SrcPos pos = node->data.asm_expr.offset_map.at(offset); + + err->line_start = pos.line; + err->column_start = pos.column; + err->line_end = -1; + err->column_end = -1; + + va_list ap; + va_start(ap, format); + err->msg = buf_vprintf(format, ap); + va_end(ap); + + err->path = pc->owner->path; + err->source = pc->owner->source_code; + err->line_offsets = pc->owner->line_offsets; + + print_err_msg(err, pc->err_color); + exit(EXIT_FAILURE); +} + __attribute__ ((format (printf, 3, 4))) __attribute__ ((noreturn)) static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { @@ -372,6 +399,7 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) { StateStart, StatePercent, StateTemplate, + StateVar, }; ZigList *tok_list = &node->data.asm_expr.token_list; @@ -403,8 +431,11 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) { if (c == '%') { cur_tok->end = i; state = StateStart; + } else if (c == '[') { + cur_tok->id = AsmTokenIdVar; + state = StateVar; } else { - zig_panic("TODO handle assembly tokenize error"); + ast_asm_error(pc, node, i, "expected a '%%' or '['"); } break; case StateTemplate: @@ -415,6 +446,19 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) { state = StateStart; } break; + case StateVar: + if (c == ']') { + cur_tok->end = i; + state = StateStart; + } else if ((c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c == '_')) + { + // do nothing + } else { + ast_asm_error(pc, node, i, "invalid substitution character: '%c'", c); + } + break; } } @@ -422,7 +466,8 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) { case StateStart: break; case StatePercent: - zig_panic("TODO handle assembly tokenize error eof"); + case StateVar: + ast_asm_error(pc, node, buf_len(asm_template), "unexpected end of assembly template"); break; case StateTemplate: cur_tok->end = buf_len(asm_template); @@ -430,40 +475,59 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) { } } -static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf) { +static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, ZigList *offset_map) { // skip the double quotes at beginning and end // convert escape sequences buf_resize(buf, 0); bool escape = false; - for (int i = token->start_pos + 1; i < token->end_pos - 1; i += 1) { + bool first = true; + SrcPos pos = {token->start_line, token->start_column}; + for (int i = token->start_pos; i < token->end_pos - 1; i += 1) { uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); - if (escape) { - switch (c) { - case '\\': - buf_append_char(buf, '\\'); - break; - case 'r': - buf_append_char(buf, '\r'); - break; - case 'n': - buf_append_char(buf, '\n'); - break; - case 't': - buf_append_char(buf, '\t'); - break; - case '"': - buf_append_char(buf, '"'); - break; - } - escape = false; - } else if (c == '\\') { - escape = true; + if (first) { + first = false; } else { - buf_append_char(buf, c); + if (escape) { + switch (c) { + case '\\': + buf_append_char(buf, '\\'); + if (offset_map) offset_map->append(pos); + break; + case 'r': + buf_append_char(buf, '\r'); + if (offset_map) offset_map->append(pos); + break; + case 'n': + buf_append_char(buf, '\n'); + if (offset_map) offset_map->append(pos); + break; + case 't': + buf_append_char(buf, '\t'); + if (offset_map) offset_map->append(pos); + break; + case '"': + buf_append_char(buf, '"'); + if (offset_map) offset_map->append(pos); + break; + } + escape = false; + } else if (c == '\\') { + escape = true; + } else { + buf_append_char(buf, c); + if (offset_map) offset_map->append(pos); + } + } + if (c == '\n') { + pos.line += 1; + pos.column = 0; + } else { + pos.column += 1; } } assert(!escape); + if (offset_map) offset_map->append(pos); } __attribute__ ((noreturn)) @@ -505,7 +569,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_ token_index += 1; ast_expect_token(pc, param_str, TokenIdStringLiteral); - parse_string_literal(pc, param_str, &node->data.directive.param); + parse_string_literal(pc, param_str, &node->data.directive.param, nullptr); Token *r_paren = &pc->tokens->at(token_index); token_index += 1; @@ -718,7 +782,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool return node; } else if (token->id == TokenIdStringLiteral) { AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token); - parse_string_literal(pc, token, &node->data.string); + parse_string_literal(pc, token, &node->data.string, nullptr); *token_index += 1; return node; } else if (token->id == TokenIdKeywordUnreachable) { @@ -861,7 +925,7 @@ static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bo *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw); - node->data.cast_expr.prefix_op_expr = prefix_op_expr; + node->data.cast_expr.expr = prefix_op_expr; node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index); @@ -1333,6 +1397,141 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand return node; } +static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id) { + Token *token = &pc->tokens->at(*token_index); + ast_expect_token(pc, token, token_id); + *token_index += 1; + return token; +} + + +/* +AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) +*/ +static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode *node) { + ast_eat_token(pc, token_index, TokenIdLBracket); + Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdRBracket); + + Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); + + ast_eat_token(pc, token_index, TokenIdLParen); + AstNode *expr_node = ast_parse_expression(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + + AsmInput *asm_input = allocate(1); + ast_buf_from_token(pc, alias, &asm_input->asm_symbolic_name); + parse_string_literal(pc, constraint, &asm_input->constraint, nullptr); + asm_input->expr = expr_node; + node->data.asm_expr.input_list.append(asm_input); +} + +/* +AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) token(Symbol) token(RParen) +*/ +static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { + ast_eat_token(pc, token_index, TokenIdLBracket); + Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdRBracket); + + Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); + + ast_eat_token(pc, token_index, TokenIdLParen); + Token *out_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdRParen); + + AsmOutput *asm_output = allocate(1); + ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name); + parse_string_literal(pc, constraint, &asm_output->constraint, nullptr); + ast_buf_from_token(pc, out_symbol, &asm_output->variable_name); + node->data.asm_expr.output_list.append(asm_output); +} + +/* +AsmClobbers: token(Colon) list(token(String), token(Comma)) +*/ +static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode *node) { + Token *colon_tok = &pc->tokens->at(*token_index); + + if (colon_tok->id != TokenIdColon) + return; + + *token_index += 1; + + for (;;) { + Token *string_tok = &pc->tokens->at(*token_index); + ast_expect_token(pc, string_tok, TokenIdStringLiteral); + *token_index += 1; + + Buf *clobber_buf = buf_alloc(); + parse_string_literal(pc, string_tok, clobber_buf, nullptr); + node->data.asm_expr.clobber_list.append(clobber_buf); + + Token *comma = &pc->tokens->at(*token_index); + + if (comma->id == TokenIdComma) { + *token_index += 1; + continue; + } else { + break; + } + } +} + +/* +AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) +*/ +static void ast_parse_asm_input(ParseContext *pc, int *token_index, AstNode *node) { + Token *colon_tok = &pc->tokens->at(*token_index); + + if (colon_tok->id != TokenIdColon) + return; + + *token_index += 1; + + for (;;) { + ast_parse_asm_input_item(pc, token_index, node); + + Token *comma = &pc->tokens->at(*token_index); + + if (comma->id == TokenIdComma) { + *token_index += 1; + continue; + } else { + break; + } + } + + ast_parse_asm_clobbers(pc, token_index, node); +} + +/* +AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) +*/ +static void ast_parse_asm_output(ParseContext *pc, int *token_index, AstNode *node) { + Token *colon_tok = &pc->tokens->at(*token_index); + + if (colon_tok->id != TokenIdColon) + return; + + *token_index += 1; + + for (;;) { + ast_parse_asm_output_item(pc, token_index, node); + + Token *comma = &pc->tokens->at(*token_index); + + if (comma->id == TokenIdComma) { + *token_index += 1; + continue; + } else { + break; + } + } + + ast_parse_asm_input(pc, token_index, node); +} + /* AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) */ @@ -1366,9 +1565,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand ast_expect_token(pc, template_tok, TokenIdStringLiteral); *token_index += 1; - parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template); + parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, + &node->data.asm_expr.offset_map); parse_asm_template(pc, node); + ast_parse_asm_output(pc, token_index, node); + Token *rparen_tok = &pc->tokens->at(*token_index); ast_expect_token(pc, rparen_tok, TokenIdRParen); *token_index += 1; @@ -1675,7 +1877,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b *token_index += 1; ast_expect_token(pc, export_name, TokenIdStringLiteral); - parse_string_literal(pc, export_name, &node->data.root_export_decl.name); + parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr); Token *semicolon = &pc->tokens->at(*token_index); *token_index += 1; @@ -1705,7 +1907,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw); - parse_string_literal(pc, use_name, &node->data.use.path); + parse_string_literal(pc, use_name, &node->data.use.path, nullptr); node->data.use.directives = pc->directive_list; pc->directive_list = nullptr; diff --git a/src/parser.hpp b/src/parser.hpp index f8b213f991..373de3178a 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -169,8 +169,7 @@ struct AstNodeRootExportDecl { }; struct AstNodeCastExpr { - AstNode *prefix_op_expr; - // if type is non-null, do cast, otherwise nothing + AstNode *expr; AstNode *type; }; @@ -205,10 +204,31 @@ struct AstNodeGoto { Buf name; }; +struct AsmOutput { + Buf asm_symbolic_name; + Buf constraint; + Buf variable_name; +}; + +struct AsmInput { + Buf asm_symbolic_name; + Buf constraint; + AstNode *expr; +}; + +struct SrcPos { + int line; + int column; +}; + struct AstNodeAsmExpr { bool is_volatile; Buf asm_template; + ZigList offset_map; ZigList token_list; + ZigList output_list; + ZigList input_list; + ZigList clobber_list; }; struct AstNode { @@ -250,6 +270,7 @@ struct AstNode { enum AsmTokenId { AsmTokenIdTemplate, AsmTokenIdPercent, + AsmTokenIdVar, }; struct AsmToken { diff --git a/std/std.zig b/std/std.zig index 3bcf9e4783..4e523e0015 100644 --- a/std/std.zig +++ b/std/std.zig @@ -1,13 +1,13 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { let mut result : isize; - asm volatile ( - "mov %[number], %%rax\n" - "mov %[arg1], %%rdi\n" - "mov %[arg2], %%rsi\n" - "mov %[arg3], %%rdx\n" - "syscall\n" - "mov %%rax, %[ret]\n" - : [ret] "=r" (result) + asm volatile (" + mov %[number], %%rax + mov %[arg1], %%rdi + mov %[arg2], %%rsi + mov %[arg3], %%rdx + syscall + mov %%rax, %[ret]" + : [ret] "=m" (result) : [number] "r" (number), [arg1] "r" (arg1), [arg2] "r" (arg2), [arg3] "r" (arg3) : "rcx", "r11", "rax", "rdi", "rsi", "rdx"); return result; @@ -16,8 +16,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { // TODO error handling // TODO zig strings instead of C strings // TODO handle buffering and flushing -pub print_str(str : *const u8, len: isize) { +// TODO non-i32 integer literals so we can remove the casts +pub fn print_str(str : *const u8, len: isize) { let SYS_write = 1; let stdout_fileno = 1; - syscall3(SYS_write, stdout_fileno, str as isize, str_len); + syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len); } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 5d28e54ebf..60c85e7d07 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -398,6 +398,18 @@ loop_2_end: } )SOURCE", "OK\n"); + + add_simple_case("hello world without libc", R"SOURCE( +use "std.zig"; + +export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { + print_str("Hello, world!\n", 14 as isize); + return 0; +} + )SOURCE", "Hello, world!\n"); + + + } static void add_compile_failure_test_cases(void) {