diff --git a/src/Sema.zig b/src/Sema.zig index a5e991c617..a769194776 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11406,6 +11406,7 @@ fn zirAsm( // Indicate the output is the asm instruction return value. arg.* = .none; const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); + try sema.queueFullTypeResolution(out_ty); expr_ty = try sema.addType(out_ty); } else { arg.* = try sema.resolveInst(output.data.operand); @@ -11430,7 +11431,10 @@ fn zirAsm( switch (uncasted_arg_ty.zigTypeTag()) { .ComptimeInt => arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted_arg, src), .ComptimeFloat => arg.* = try sema.coerce(block, Type.initTag(.f64), uncasted_arg, src), - else => arg.* = uncasted_arg, + else => { + arg.* = uncasted_arg; + try sema.queueFullTypeResolution(uncasted_arg_ty); + }, } const constraint = sema.code.nullTerminatedString(input.data.constraint); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index efe6a754b6..bf09ec0f35 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5421,6 +5421,8 @@ pub const FuncGen = struct { const llvm_params_len = inputs.len + outputs.len - return_count; const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); + const target = self.dg.module.getTarget(); + var llvm_param_i: usize = 0; var total_i: usize = 0; @@ -5449,7 +5451,18 @@ pub const FuncGen = struct { llvm_param_types[llvm_param_i] = output_inst.typeOf(); llvm_param_i += 1; } - llvm_constraints.appendSliceAssumeCapacity(constraint[1..]); + + // 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 + for (constraint[1..]) |byte| { + llvm_constraints.appendAssumeCapacity(switch (byte) { + ',' => '|', + else => byte, + }); + } name_map.putAssumeCapacityNoClobber(name, {}); total_i += 1; @@ -5464,15 +5477,43 @@ pub const FuncGen = struct { extra_i += (constraint.len + name.len + (2 + 3)) / 4; const arg_llvm_value = try self.resolveInst(input); - - llvm_param_values[llvm_param_i] = arg_llvm_value; - llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); + const arg_ty = self.air.typeOf(input); + if (isByRef(arg_ty)) { + if (constraintAllowsMemory(constraint)) { + llvm_param_values[llvm_param_i] = arg_llvm_value; + llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); + } else { + const alignment = arg_ty.abiAlignment(target); + const load_inst = self.builder.buildLoad(arg_llvm_value, ""); + load_inst.setAlignment(alignment); + llvm_param_values[llvm_param_i] = load_inst; + llvm_param_types[llvm_param_i] = load_inst.typeOf(); + } + } else { + if (constraintAllowsRegister(constraint)) { + llvm_param_values[llvm_param_i] = arg_llvm_value; + llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); + } else { + const alignment = arg_ty.abiAlignment(target); + const arg_ptr = self.buildAlloca(arg_llvm_value.typeOf()); + arg_ptr.setAlignment(alignment); + const store_inst = self.builder.buildStore(arg_llvm_value, arg_ptr); + store_inst.setAlignment(alignment); + llvm_param_values[llvm_param_i] = arg_ptr; + llvm_param_types[llvm_param_i] = arg_ptr.typeOf(); + } + } try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); if (total_i != 0) { llvm_constraints.appendAssumeCapacity(','); } - llvm_constraints.appendSliceAssumeCapacity(constraint); + for (constraint) |byte| { + llvm_constraints.appendAssumeCapacity(switch (byte) { + ',' => '|', + else => byte, + }); + } if (!std.mem.eql(u8, name, "_")) { name_map.putAssumeCapacityNoClobber(name, {}); @@ -9307,3 +9348,11 @@ fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u1 { fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u1 { return @boolToInt(Type.anyerror.abiAlignment(target) <= payload_ty.abiAlignment(target)); } + +fn constraintAllowsMemory(constraint: []const u8) bool { + return constraint[0] == 'm'; +} + +fn constraintAllowsRegister(constraint: []const u8) bool { + return constraint[0] != 'm'; +} diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index 9aa95a3a0b..8f235a384b 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -35,7 +35,6 @@ test "output constraint modifiers" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO // This is only testing compilation. var a: u32 = 3; @@ -57,7 +56,6 @@ test "alternative constraints" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO // Make sure we allow commas as a separator for alternative constraints. var a: u32 = 3; @@ -122,7 +120,6 @@ test "struct/array/union types as input values" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO asm volatile ("" :