authorgravatar for Sahnvour@users.noreply.github.comSahnvour <Sahnvour@users.noreply.github.com> 2018-11-19 22:22:21+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-11-19 16:22:21-05:00
log703c6684d103f14193411b589eaa0e0b1e1189f0
tree3c19fddb4174bb5ed104e6dbac573533821956cd
parent89e82281be73701f5a634770366e9dca5a0e89a9

Crash fixes and small improvements to inline asm. (#1756)

* codegen: LLVMConstInlineAsm is deprecated. * codegen: replace commas in asm constraint strings by pipes as required by LLVM. * ir: enforce usage of '=' constraint modifier for inline assembly outputs. Others are not currently supported and this was just asserted alter in `ir_render_asm`. * asm: forbid comptime_int/floats as inputs in favor of explicitely sized constants. Fixes a crash due to comptime_int/floats having no type_ref. * asm: handle inputs with integers of <8 or non power of 2 bitsize. We widen them to the next highest power of two.

6 files changed, 121 insertions(+), 6 deletions(-)

src/buffer.hpp+10
...@@ -181,5 +181,15 @@ static inline Slice<uint8_t> buf_to_slice(Buf *buf) {...@@ -181,5 +181,15 @@ static inline Slice<uint8_t> buf_to_slice(Buf *buf) {
181 return Slice<uint8_t>{reinterpret_cast<uint8_t*>(buf_ptr(buf)), buf_len(buf)};181 return Slice<uint8_t>{reinterpret_cast<uint8_t*>(buf_ptr(buf)), buf_len(buf)};
182}182}
183183
184static inline void buf_replace(Buf* buf, char from, char to) {
185 const size_t count = buf_len(buf);
186 char* ptr = buf_ptr(buf);
187 for (size_t i = 0; i < count; ++i) {
188 char& l = ptr[i];
189 if (l == from)
190 l = to;
191 }
192}
193
184194
185#endif195#endif
src/codegen.cpp+27-4
...@@ -3660,6 +3660,13 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3660,6 +3660,13 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
3660 AsmOutput *asm_output = asm_expr->output_list.at(i);3660 AsmOutput *asm_output = asm_expr->output_list.at(i);
3661 bool is_return = (asm_output->return_type != nullptr);3661 bool is_return = (asm_output->return_type != nullptr);
3662 assert(*buf_ptr(asm_output->constraint) == '=');3662 assert(*buf_ptr(asm_output->constraint) == '=');
3663 // LLVM uses commas internally to separate different constraints,
3664 // alternative constraints are achieved with pipes.
3665 // We still allow the user to use commas in a way that is similar
3666 // to GCC's inline assembly.
3667 // http://llvm.org/docs/LangRef.html#constraint-codes
3668 buf_replace(asm_output->constraint, ',', '|');
3669
3663 if (is_return) {3670 if (is_return) {
3664 buf_appendf(&constraint_buf, "=%s", buf_ptr(asm_output->constraint) + 1);3671 buf_appendf(&constraint_buf, "=%s", buf_ptr(asm_output->constraint) + 1);
3665 } else {3672 } else {
...@@ -3679,14 +3686,30 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3679,14 +3686,30 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
3679 }3686 }
3680 for (size_t i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) {3687 for (size_t i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) {
3681 AsmInput *asm_input = asm_expr->input_list.at(i);3688 AsmInput *asm_input = asm_expr->input_list.at(i);
3689 buf_replace(asm_input->constraint, ',', '|');
3682 IrInstruction *ir_input = instruction->input_list[i];3690 IrInstruction *ir_input = instruction->input_list[i];
3683 buf_append_buf(&constraint_buf, asm_input->constraint);3691 buf_append_buf(&constraint_buf, asm_input->constraint);
3684 if (total_index + 1 < total_constraint_count) {3692 if (total_index + 1 < total_constraint_count) {
3685 buf_append_char(&constraint_buf, ',');3693 buf_append_char(&constraint_buf, ',');
3686 }3694 }
36873695
3688 param_types[param_index] = ir_input->value.type->type_ref;3696 ZigType *const type = ir_input->value.type;
3689 param_values[param_index] = ir_llvm_value(g, ir_input);3697 LLVMTypeRef type_ref = type->type_ref;
3698 LLVMValueRef value_ref = ir_llvm_value(g, ir_input);
3699 // Handle integers of non pot bitsize by widening them.
3700 if (type->id == ZigTypeIdInt) {
3701 const size_t bitsize = type->data.integral.bit_count;
3702 if (bitsize < 8 || !is_power_of_2(bitsize)) {
3703 const bool is_signed = type->data.integral.is_signed;
3704 const size_t wider_bitsize = bitsize < 8 ? 8 : round_to_next_power_of_2(bitsize);
3705 ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize);
3706 type_ref = wider_type->type_ref;
3707 value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref);
3708 }
3709 }
3710
3711 param_types[param_index] = type_ref;
3712 param_values[param_index] = value_ref;
3690 }3713 }
3691 for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) {3714 for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) {
3692 Buf *clobber_buf = asm_expr->clobber_list.at(i);3715 Buf *clobber_buf = asm_expr->clobber_list.at(i);
...@@ -3705,8 +3728,8 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3705,8 +3728,8 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
3705 LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false);3728 LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false);
37063729
3707 bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0);3730 bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0);
3708 LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template),3731 LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template),
3709 buf_ptr(&constraint_buf), is_volatile, false);3732 buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT);
37103733
3711 return LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, "");3734 return LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, "");
3712}3735}
src/ir.cpp+21-2
...@@ -5537,6 +5537,15 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -5537,6 +5537,15 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
5537 return irb->codegen->invalid_instruction;5537 return irb->codegen->invalid_instruction;
5538 }5538 }
5539 }5539 }
5540
5541 const char modifier = *buf_ptr(asm_output->constraint);
5542 if (modifier != '=') {
5543 add_node_error(irb->codegen, node,
5544 buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported."
5545 " Compiler TODO: see https://github.com/ziglang/zig/issues/215",
5546 buf_ptr(asm_output->asm_symbolic_name), modifier));
5547 return irb->codegen->invalid_instruction;
5548 }
5540 }5549 }
5541 for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {5550 for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
5542 AsmInput *asm_input = node->data.asm_expr.input_list.at(i);5551 AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
...@@ -15386,9 +15395,19 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs...@@ -15386,9 +15395,19 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs
15386 }15395 }
1538715396
15388 for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {15397 for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
15389 input_list[i] = asm_instruction->input_list[i]->child;15398 IrInstruction *const input_value = asm_instruction->input_list[i]->child;
15390 if (type_is_invalid(input_list[i]->value.type))15399 if (type_is_invalid(input_value->value.type))
15391 return ira->codegen->invalid_instruction;15400 return ira->codegen->invalid_instruction;
15401
15402 if (instr_is_comptime(input_value) &&
15403 (input_value->value.type->id == ZigTypeIdComptimeInt ||
15404 input_value->value.type->id == ZigTypeIdComptimeFloat)) {
15405 ir_add_error_node(ira, input_value->source_node,
15406 buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name)));
15407 return ira->codegen->invalid_instruction;
15408 }
15409
15410 input_list[i] = input_value;
15392 }15411 }
1539315412
15394 IrInstruction *result = ir_build_asm(&ira->new_irb,15413 IrInstruction *result = ir_build_asm(&ira->new_irb,
src/util.hpp+11
...@@ -158,6 +158,17 @@ static inline bool is_power_of_2(uint64_t x) {...@@ -158,6 +158,17 @@ static inline bool is_power_of_2(uint64_t x) {
158 return x != 0 && ((x & (~x + 1)) == x);158 return x != 0 && ((x & (~x + 1)) == x);
159}159}
160160
161static inline uint64_t round_to_next_power_of_2(uint64_t x) {
162 --x;
163 x |= x >> 1;
164 x |= x >> 2;
165 x |= x >> 4;
166 x |= x >> 8;
167 x |= x >> 16;
168 x |= x >> 32;
169 return x + 1;
170}
171
161uint32_t int_hash(int i);172uint32_t int_hash(int i);
162bool int_eq(int a, int b);173bool int_eq(int a, int b);
163uint32_t uint64_hash(uint64_t i);174uint32_t uint64_hash(uint64_t i);
test/cases/asm.zig+24
...@@ -17,6 +17,30 @@ test "module level assembly" {...@@ -17,6 +17,30 @@ test "module level assembly" {
17 }17 }
18}18}
1919
20test "output constraint modifiers" {
21 // This is only testing compilation.
22 var a: u32 = 3;
23 asm volatile ("" : [_]"=m,r"(a) : : "");
24 asm volatile ("" : [_]"=r,m"(a) : : "");
25}
26
27test "alternative constraints" {
28 // Make sure we allow commas as a separator for alternative constraints.
29 var a: u32 = 3;
30 asm volatile ("" : [_]"=r,m"(a) : [_]"r,m"(a) : "");
31}
32
33test "sized integer/float in asm input" {
34 asm volatile ("" : : [_]"m"(usize(3)) : "");
35 asm volatile ("" : : [_]"m"(i15(-3)) : "");
36 asm volatile ("" : : [_]"m"(u3(3)) : "");
37 asm volatile ("" : : [_]"m"(i3(3)) : "");
38 asm volatile ("" : : [_]"m"(u121(3)) : "");
39 asm volatile ("" : : [_]"m"(i121(3)) : "");
40 asm volatile ("" : : [_]"m"(f32(3.17)) : "");
41 asm volatile ("" : : [_]"m"(f64(3.17)) : "");
42}
43
20extern fn aoeu() i32;44extern fn aoeu() i32;
2145
22export fn derp() i32 {46export fn derp() i32 {
test/compile_errors.zig+28
...@@ -5232,4 +5232,32 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5232,4 +5232,32 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5232 ,5232 ,
5233 ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",5233 ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",
5234 );5234 );
5235
5236 cases.add(
5237 "unsupported modifier at start of asm output constraint",
5238 \\export fn foo() void {
5239 \\ var bar: u32 = 3;
5240 \\ asm volatile ("" : [baz]"+r"(bar) : : "");
5241 \\}
5242 ,
5243 ".tmp_source.zig:3:5: error: invalid modifier starting output constraint for 'baz': '+', only '=' is supported. Compiler TODO: see https://github.com/ziglang/zig/issues/215",
5244 );
5245
5246 cases.add(
5247 "comptime_int in asm input",
5248 \\export fn foo() void {
5249 \\ asm volatile ("" : : [bar]"r"(3) : "");
5250 \\}
5251 ,
5252 ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int",
5253 );
5254
5255 cases.add(
5256 "comptime_float in asm input",
5257 \\export fn foo() void {
5258 \\ asm volatile ("" : : [bar]"r"(3.17) : "");
5259 \\}
5260 ,
5261 ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float",
5262 );
5235}5263}