Commit 703c6684d1
Changed files (6)
src/buffer.hpp
@@ -181,5 +181,15 @@ static inline Slice<uint8_t> buf_to_slice(Buf *buf) {
return Slice<uint8_t>{reinterpret_cast<uint8_t*>(buf_ptr(buf)), buf_len(buf)};
}
+static inline void buf_replace(Buf* buf, char from, char to) {
+ const size_t count = buf_len(buf);
+ char* ptr = buf_ptr(buf);
+ for (size_t i = 0; i < count; ++i) {
+ char& l = ptr[i];
+ if (l == from)
+ l = to;
+ }
+}
+
#endif
src/codegen.cpp
@@ -3660,6 +3660,13 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
AsmOutput *asm_output = asm_expr->output_list.at(i);
bool is_return = (asm_output->return_type != nullptr);
assert(*buf_ptr(asm_output->constraint) == '=');
+ // LLVM uses commas internally to separate different constraints,
+ // alternative constraints are achieved with pipes.
+ // We still allow the user to use commas in a way that is similar
+ // to GCC's inline assembly.
+ // http://llvm.org/docs/LangRef.html#constraint-codes
+ buf_replace(asm_output->constraint, ',', '|');
+
if (is_return) {
buf_appendf(&constraint_buf, "=%s", buf_ptr(asm_output->constraint) + 1);
} else {
@@ -3679,14 +3686,30 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
}
for (size_t i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) {
AsmInput *asm_input = asm_expr->input_list.at(i);
+ buf_replace(asm_input->constraint, ',', '|');
IrInstruction *ir_input = instruction->input_list[i];
buf_append_buf(&constraint_buf, asm_input->constraint);
if (total_index + 1 < total_constraint_count) {
buf_append_char(&constraint_buf, ',');
}
- param_types[param_index] = ir_input->value.type->type_ref;
- param_values[param_index] = ir_llvm_value(g, ir_input);
+ ZigType *const type = ir_input->value.type;
+ LLVMTypeRef type_ref = type->type_ref;
+ LLVMValueRef value_ref = ir_llvm_value(g, ir_input);
+ // Handle integers of non pot bitsize by widening them.
+ if (type->id == ZigTypeIdInt) {
+ const size_t bitsize = type->data.integral.bit_count;
+ if (bitsize < 8 || !is_power_of_2(bitsize)) {
+ const bool is_signed = type->data.integral.is_signed;
+ const size_t wider_bitsize = bitsize < 8 ? 8 : round_to_next_power_of_2(bitsize);
+ ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize);
+ type_ref = wider_type->type_ref;
+ value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref);
+ }
+ }
+
+ param_types[param_index] = type_ref;
+ param_values[param_index] = value_ref;
}
for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) {
Buf *clobber_buf = asm_expr->clobber_list.at(i);
@@ -3705,8 +3728,8 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)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);
+ LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template),
+ buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT);
return LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, "");
}
src/ir.cpp
@@ -5537,6 +5537,15 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
return irb->codegen->invalid_instruction;
}
}
+
+ const char modifier = *buf_ptr(asm_output->constraint);
+ if (modifier != '=') {
+ add_node_error(irb->codegen, node,
+ buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported."
+ " Compiler TODO: see https://github.com/ziglang/zig/issues/215",
+ buf_ptr(asm_output->asm_symbolic_name), modifier));
+ return irb->codegen->invalid_instruction;
+ }
}
for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
@@ -15386,9 +15395,19 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs
}
for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
- input_list[i] = asm_instruction->input_list[i]->child;
- if (type_is_invalid(input_list[i]->value.type))
+ IrInstruction *const input_value = asm_instruction->input_list[i]->child;
+ if (type_is_invalid(input_value->value.type))
return ira->codegen->invalid_instruction;
+
+ if (instr_is_comptime(input_value) &&
+ (input_value->value.type->id == ZigTypeIdComptimeInt ||
+ input_value->value.type->id == ZigTypeIdComptimeFloat)) {
+ ir_add_error_node(ira, input_value->source_node,
+ buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ input_list[i] = input_value;
}
IrInstruction *result = ir_build_asm(&ira->new_irb,
src/util.hpp
@@ -158,6 +158,17 @@ static inline bool is_power_of_2(uint64_t x) {
return x != 0 && ((x & (~x + 1)) == x);
}
+static inline uint64_t round_to_next_power_of_2(uint64_t x) {
+ --x;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ x |= x >> 32;
+ return x + 1;
+}
+
uint32_t int_hash(int i);
bool int_eq(int a, int b);
uint32_t uint64_hash(uint64_t i);
test/cases/asm.zig
@@ -17,6 +17,30 @@ test "module level assembly" {
}
}
+test "output constraint modifiers" {
+ // This is only testing compilation.
+ var a: u32 = 3;
+ asm volatile ("" : [_]"=m,r"(a) : : "");
+ asm volatile ("" : [_]"=r,m"(a) : : "");
+}
+
+test "alternative constraints" {
+ // Make sure we allow commas as a separator for alternative constraints.
+ var a: u32 = 3;
+ asm volatile ("" : [_]"=r,m"(a) : [_]"r,m"(a) : "");
+}
+
+test "sized integer/float in asm input" {
+ asm volatile ("" : : [_]"m"(usize(3)) : "");
+ asm volatile ("" : : [_]"m"(i15(-3)) : "");
+ asm volatile ("" : : [_]"m"(u3(3)) : "");
+ asm volatile ("" : : [_]"m"(i3(3)) : "");
+ asm volatile ("" : : [_]"m"(u121(3)) : "");
+ asm volatile ("" : : [_]"m"(i121(3)) : "");
+ asm volatile ("" : : [_]"m"(f32(3.17)) : "");
+ asm volatile ("" : : [_]"m"(f64(3.17)) : "");
+}
+
extern fn aoeu() i32;
export fn derp() i32 {
test/compile_errors.zig
@@ -5232,4 +5232,32 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
,
".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",
);
+
+ cases.add(
+ "unsupported modifier at start of asm output constraint",
+ \\export fn foo() void {
+ \\ var bar: u32 = 3;
+ \\ asm volatile ("" : [baz]"+r"(bar) : : "");
+ \\}
+ ,
+ ".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",
+ );
+
+ cases.add(
+ "comptime_int in asm input",
+ \\export fn foo() void {
+ \\ asm volatile ("" : : [bar]"r"(3) : "");
+ \\}
+ ,
+ ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int",
+ );
+
+ cases.add(
+ "comptime_float in asm input",
+ \\export fn foo() void {
+ \\ asm volatile ("" : : [bar]"r"(3.17) : "");
+ \\}
+ ,
+ ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float",
+ );
}