| author | |
| committer | |
| log | a292eb8d64da4383f2d4637f231d338ed9c680e0 |
| tree | 1a368a2cc65675b793651a8da83b6cd12b8a4bca |
| parent | 66ca916805efee6b35b8dd6104fb1da50dd1dc8b |
6 files changed, 64 insertions(+), 27 deletions(-)
doc/langref.md+1-1| ... | @@ -84,7 +84,7 @@ AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) | ... | @@ -84,7 +84,7 @@ AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) |
| 84 | 84 | ||
| 85 | AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) | 85 | AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) |
| 86 | 86 | ||
| 87 | AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) token(Symbol) token(RParen) | 87 | AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Return) Type) token(RParen) |
| 88 | 88 | ||
| 89 | AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) | 89 | AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) |
| 90 | 90 |
src/analyze.cpp+13-2| ... | @@ -1233,16 +1233,27 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -1233,16 +1233,27 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1233 | } | 1233 | } |
| 1234 | case NodeTypeAsmExpr: | 1234 | case NodeTypeAsmExpr: |
| 1235 | { | 1235 | { |
| 1236 | node->data.asm_expr.return_count = 0; | ||
| 1237 | return_type = g->builtin_types.entry_void; | ||
| 1236 | for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { | 1238 | for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { |
| 1237 | AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); | 1239 | AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); |
| 1238 | analyze_variable_name(g, import, context, node, &asm_output->variable_name); | 1240 | if (asm_output->return_type) { |
| 1241 | node->data.asm_expr.return_count += 1; | ||
| 1242 | return_type = resolve_type(g, asm_output->return_type); | ||
| 1243 | if (node->data.asm_expr.return_count > 1) { | ||
| 1244 | add_node_error(g, node, | ||
| 1245 | buf_sprintf("inline assembly allows up to one output value")); | ||
| 1246 | break; | ||
| 1247 | } | ||
| 1248 | } else { | ||
| 1249 | analyze_variable_name(g, import, context, node, &asm_output->variable_name); | ||
| 1250 | } | ||
| 1239 | } | 1251 | } |
| 1240 | for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) { | 1252 | for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) { |
| 1241 | AsmInput *asm_input = node->data.asm_expr.input_list.at(i); | 1253 | AsmInput *asm_input = node->data.asm_expr.input_list.at(i); |
| 1242 | analyze_expression(g, import, context, nullptr, asm_input->expr); | 1254 | analyze_expression(g, import, context, nullptr, asm_input->expr); |
| 1243 | } | 1255 | } |
| 1244 | 1256 | ||
| 1245 | return_type = g->builtin_types.entry_void; | ||
| 1246 | break; | 1257 | break; |
| 1247 | } | 1258 | } |
| 1248 | case NodeTypeBinOpExpr: | 1259 | case NodeTypeBinOpExpr: |
src/codegen.cpp+30-12| ... | @@ -790,33 +790,45 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { | ... | @@ -790,33 +790,45 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { |
| 790 | 790 | ||
| 791 | Buf constraint_buf = BUF_INIT; | 791 | Buf constraint_buf = BUF_INIT; |
| 792 | buf_resize(&constraint_buf, 0); | 792 | buf_resize(&constraint_buf, 0); |
| 793 | |||
| 794 | assert(asm_expr->return_count == 0 || asm_expr->return_count == 1); | ||
| 795 | |||
| 793 | int total_constraint_count = asm_expr->output_list.length + | 796 | int total_constraint_count = asm_expr->output_list.length + |
| 794 | asm_expr->input_list.length + | 797 | asm_expr->input_list.length + |
| 795 | asm_expr->clobber_list.length; | 798 | asm_expr->clobber_list.length; |
| 796 | int input_and_output_count = asm_expr->output_list.length + | 799 | int input_and_output_count = asm_expr->output_list.length + |
| 797 | asm_expr->input_list.length; | 800 | asm_expr->input_list.length - |
| 801 | asm_expr->return_count; | ||
| 798 | int total_index = 0; | 802 | int total_index = 0; |
| 803 | int param_index = 0; | ||
| 799 | LLVMTypeRef *param_types = allocate<LLVMTypeRef>(input_and_output_count); | 804 | LLVMTypeRef *param_types = allocate<LLVMTypeRef>(input_and_output_count); |
| 800 | LLVMValueRef *param_values = allocate<LLVMValueRef>(input_and_output_count); | 805 | LLVMValueRef *param_values = allocate<LLVMValueRef>(input_and_output_count); |
| 801 | for (int i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) { | 806 | for (int i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) { |
| 802 | AsmOutput *asm_output = asm_expr->output_list.at(i); | 807 | AsmOutput *asm_output = asm_expr->output_list.at(i); |
| 808 | bool is_return = false; | ||
| 803 | if (buf_eql_str(&asm_output->constraint, "=m")) { | 809 | if (buf_eql_str(&asm_output->constraint, "=m")) { |
| 804 | buf_append_str(&constraint_buf, "=*m"); | 810 | buf_append_str(&constraint_buf, "=*m"); |
| 811 | } else if (buf_eql_str(&asm_output->constraint, "=r")) { | ||
| 812 | buf_append_str(&constraint_buf, "=r"); | ||
| 813 | is_return = true; | ||
| 805 | } else { | 814 | } else { |
| 806 | zig_panic("TODO unable to handle anything other than '=m' for outputs"); | 815 | zig_panic("TODO unable to handle anything other than '=m' and '=r' for outputs"); |
| 807 | } | 816 | } |
| 808 | if (total_index + 1 < total_constraint_count) { | 817 | if (total_index + 1 < total_constraint_count) { |
| 809 | buf_append_char(&constraint_buf, ','); | 818 | buf_append_char(&constraint_buf, ','); |
| 810 | } | 819 | } |
| 811 | 820 | ||
| 812 | VariableTableEntry *variable = find_variable( | 821 | if (!is_return) { |
| 813 | node->codegen_node->expr_node.block_context, | 822 | VariableTableEntry *variable = find_variable( |
| 814 | &asm_output->variable_name); | 823 | node->codegen_node->expr_node.block_context, |
| 815 | assert(variable); | 824 | &asm_output->variable_name); |
| 816 | param_types[total_index] = LLVMTypeOf(variable->value_ref); | 825 | assert(variable); |
| 817 | param_values[total_index] = variable->value_ref; | 826 | param_types[param_index] = LLVMTypeOf(variable->value_ref); |
| 827 | param_values[param_index] = variable->value_ref; | ||
| 828 | param_index += 1; | ||
| 829 | } | ||
| 818 | } | 830 | } |
| 819 | for (int i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1) { | 831 | for (int i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) { |
| 820 | AsmInput *asm_input = asm_expr->input_list.at(i); | 832 | AsmInput *asm_input = asm_expr->input_list.at(i); |
| 821 | buf_append_buf(&constraint_buf, &asm_input->constraint); | 833 | buf_append_buf(&constraint_buf, &asm_input->constraint); |
| 822 | if (total_index + 1 < total_constraint_count) { | 834 | if (total_index + 1 < total_constraint_count) { |
| ... | @@ -824,8 +836,8 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { | ... | @@ -824,8 +836,8 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { |
| 824 | } | 836 | } |
| 825 | 837 | ||
| 826 | TypeTableEntry *expr_type = get_expr_type(asm_input->expr); | 838 | TypeTableEntry *expr_type = get_expr_type(asm_input->expr); |
| 827 | param_types[total_index] = expr_type->type_ref; | 839 | param_types[param_index] = expr_type->type_ref; |
| 828 | param_values[total_index] = gen_expr(g, asm_input->expr); | 840 | param_values[param_index] = gen_expr(g, asm_input->expr); |
| 829 | } | 841 | } |
| 830 | for (int i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) { | 842 | for (int i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) { |
| 831 | Buf *clobber_buf = asm_expr->clobber_list.at(i); | 843 | Buf *clobber_buf = asm_expr->clobber_list.at(i); |
| ... | @@ -835,7 +847,13 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { | ... | @@ -835,7 +847,13 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { |
| 835 | } | 847 | } |
| 836 | } | 848 | } |
| 837 | 849 | ||
| 838 | LLVMTypeRef function_type = LLVMFunctionType(LLVMVoidType(), param_types, input_and_output_count, false); | 850 | LLVMTypeRef ret_type; |
| 851 | if (asm_expr->return_count == 0) { | ||
| 852 | ret_type = LLVMVoidType(); | ||
| 853 | } else { | ||
| 854 | ret_type = get_expr_type(node)->type_ref; | ||
| 855 | } | ||
| 856 | LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, input_and_output_count, false); | ||
| 839 | 857 | ||
| 840 | bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0); | 858 | bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0); |
| 841 | LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), | 859 | LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), |
src/parser.cpp+14-4| ... | @@ -1731,7 +1731,7 @@ static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode | ... | @@ -1731,7 +1731,7 @@ static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode |
| 1731 | } | 1731 | } |
| 1732 | 1732 | ||
| 1733 | /* | 1733 | /* |
| 1734 | AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) token(Symbol) token(RParen) | 1734 | AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Return) Type) token(RParen) |
| 1735 | */ | 1735 | */ |
| 1736 | static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { | 1736 | static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { |
| 1737 | ast_eat_token(pc, token_index, TokenIdLBracket); | 1737 | ast_eat_token(pc, token_index, TokenIdLBracket); |
| ... | @@ -1740,14 +1740,24 @@ static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNod | ... | @@ -1740,14 +1740,24 @@ static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNod |
| 1740 | 1740 | ||
| 1741 | Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); | 1741 | Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); |
| 1742 | 1742 | ||
| 1743 | AsmOutput *asm_output = allocate<AsmOutput>(1); | ||
| 1744 | |||
| 1743 | ast_eat_token(pc, token_index, TokenIdLParen); | 1745 | ast_eat_token(pc, token_index, TokenIdLParen); |
| 1744 | Token *out_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); | 1746 | |
| 1747 | Token *token = &pc->tokens->at(*token_index); | ||
| 1748 | *token_index += 1; | ||
| 1749 | if (token->id == TokenIdSymbol) { | ||
| 1750 | ast_buf_from_token(pc, token, &asm_output->variable_name); | ||
| 1751 | } else if (token->id == TokenIdKeywordReturn) { | ||
| 1752 | asm_output->return_type = ast_parse_type(pc, token_index); | ||
| 1753 | } else { | ||
| 1754 | ast_invalid_token_error(pc, token); | ||
| 1755 | } | ||
| 1756 | |||
| 1745 | ast_eat_token(pc, token_index, TokenIdRParen); | 1757 | ast_eat_token(pc, token_index, TokenIdRParen); |
| 1746 | 1758 | ||
| 1747 | AsmOutput *asm_output = allocate<AsmOutput>(1); | ||
| 1748 | ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name); | 1759 | ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name); |
| 1749 | parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr); | 1760 | parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr); |
| 1750 | ast_buf_from_token(pc, out_symbol, &asm_output->variable_name); | ||
| 1751 | node->data.asm_expr.output_list.append(asm_output); | 1761 | node->data.asm_expr.output_list.append(asm_output); |
| 1752 | } | 1762 | } |
| 1753 | 1763 |
src/parser.hpp+2| ... | @@ -228,6 +228,7 @@ struct AsmOutput { | ... | @@ -228,6 +228,7 @@ struct AsmOutput { |
| 228 | Buf asm_symbolic_name; | 228 | Buf asm_symbolic_name; |
| 229 | Buf constraint; | 229 | Buf constraint; |
| 230 | Buf variable_name; | 230 | Buf variable_name; |
| 231 | AstNode *return_type; // null unless "=r" and return | ||
| 231 | }; | 232 | }; |
| 232 | 233 | ||
| 233 | struct AsmInput { | 234 | struct AsmInput { |
| ... | @@ -249,6 +250,7 @@ struct AstNodeAsmExpr { | ... | @@ -249,6 +250,7 @@ struct AstNodeAsmExpr { |
| 249 | ZigList<AsmOutput*> output_list; | 250 | ZigList<AsmOutput*> output_list; |
| 250 | ZigList<AsmInput*> input_list; | 251 | ZigList<AsmInput*> input_list; |
| 251 | ZigList<Buf*> clobber_list; | 252 | ZigList<Buf*> clobber_list; |
| 253 | int return_count; // populated by analyze | ||
| 252 | }; | 254 | }; |
| 253 | 255 | ||
| 254 | struct AstNodeStructDecl { | 256 | struct AstNodeStructDecl { |
std/std.zig+4-8| ... | @@ -3,20 +3,17 @@ const SYS_exit : isize = 60; | ... | @@ -3,20 +3,17 @@ const SYS_exit : isize = 60; |
| 3 | const stdout_fileno : isize = 1; | 3 | const stdout_fileno : isize = 1; |
| 4 | 4 | ||
| 5 | fn syscall1(number: isize, arg1: isize) -> isize { | 5 | fn syscall1(number: isize, arg1: isize) -> isize { |
| 6 | var result : isize; | ||
| 7 | asm volatile (" | 6 | asm volatile (" |
| 8 | mov %[number], %%rax | 7 | mov %[number], %%rax |
| 9 | mov %[arg1], %%rdi | 8 | mov %[arg1], %%rdi |
| 10 | syscall | 9 | syscall |
| 11 | mov %%rax, %[ret]" | 10 | mov %%rax, %[ret]" |
| 12 | : [ret] "=m" (result) | 11 | : [ret] "=r" (return isize) |
| 13 | : [number] "r" (number), [arg1] "r" (arg1) | 12 | : [number] "r" (number), [arg1] "r" (arg1) |
| 14 | : "rcx", "r11", "rax", "rdi"); | 13 | : "rcx", "r11", "rax", "rdi") |
| 15 | return result; | ||
| 16 | } | 14 | } |
| 17 | 15 | ||
| 18 | fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { | 16 | fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { |
| 19 | var result : isize; | ||
| 20 | asm volatile (" | 17 | asm volatile (" |
| 21 | mov %[number], %%rax | 18 | mov %[number], %%rax |
| 22 | mov %[arg1], %%rdi | 19 | mov %[arg1], %%rdi |
| ... | @@ -24,10 +21,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { | ... | @@ -24,10 +21,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { |
| 24 | mov %[arg3], %%rdx | 21 | mov %[arg3], %%rdx |
| 25 | syscall | 22 | syscall |
| 26 | mov %%rax, %[ret]" | 23 | mov %%rax, %[ret]" |
| 27 | : [ret] "=m" (result) | 24 | : [ret] "=r" (return isize) |
| 28 | : [number] "r" (number), [arg1] "r" (arg1), [arg2] "r" (arg2), [arg3] "r" (arg3) | 25 | : [number] "r" (number), [arg1] "r" (arg1), [arg2] "r" (arg2), [arg3] "r" (arg3) |
| 29 | : "rcx", "r11", "rax", "rdi", "rsi", "rdx"); | 26 | : "rcx", "r11", "rax", "rdi", "rsi", "rdx") |
| 30 | return result; | ||
| 31 | } | 27 | } |
| 32 | 28 | ||
| 33 | pub fn write(fd: isize, buf: &const u8, count: usize) -> isize { | 29 | pub fn write(fd: isize, buf: &const u8, count: usize) -> isize { |