authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-03-31 00:04:04+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-31 00:04:04+03:00
log3c64c519e65861868e05c47de2d8605a579edf06
tree9e06748f9c048b4f54982db9163f6e46e8eb9746
parentd227f76afbacb619152ffd943cf3018e62e00adc
parent907dc1e13f657f349dbdecf739b2f1a13ad7011a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11246 from jmc-88/cbe-asm

CBE: improve support for asm inputs

5 files changed, 81 insertions(+), 47 deletions(-)

src/AstGen.zig+17-35
......@@ -6423,7 +6423,6 @@ fn identifier(
64236423
64246424 const astgen = gz.astgen;
64256425 const tree = astgen.tree;
6426 const gpa = astgen.gpa;
64276426 const main_tokens = tree.nodes.items(.main_token);
64286427
64296428 const ident_token = main_tokens[ident];
......@@ -6467,6 +6466,19 @@ fn identifier(
64676466 }
64686467
64696468 // Local variables, including function parameters.
6469 return localVarRef(gz, scope, rl, ident, ident_token);
6470}
6471
6472fn localVarRef(
6473 gz: *GenZir,
6474 scope: *Scope,
6475 rl: ResultLoc,
6476 ident: Ast.Node.Index,
6477 ident_token: Ast.Node.Index,
6478) InnerError!Zir.Inst.Ref {
6479 const astgen = gz.astgen;
6480 const gpa = astgen.gpa;
6481
64706482 const name_str_index = try astgen.identAsString(ident_token);
64716483 var s = scope;
64726484 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
......@@ -6808,43 +6820,13 @@ fn asmExpr(
68086820 };
68096821 } else {
68106822 const ident_token = symbolic_name + 4;
6811 const str_index = try astgen.identAsString(ident_token);
6812 // TODO this needs extra code for local variables. Have a look at #215 and related
6813 // issues and decide how to handle outputs. Do we want this to be identifiers?
6823 // TODO have a look at #215 and related issues and decide how to
6824 // handle outputs. Do we want this to be identifiers?
68146825 // Or maybe we want to force this to be expressions with a pointer type.
6815 // Until that is figured out this is only hooked up for referencing Decls.
6816 // TODO we have put this as an identifier lookup just so that we don't get
6817 // unused vars for outputs. We need to check if this is correct in the future ^^
6818 // so we just put in this simple lookup. This is a workaround.
6819 {
6820 var s = scope;
6821 while (true) switch (s.tag) {
6822 .local_val => {
6823 const local_val = s.cast(Scope.LocalVal).?;
6824 if (local_val.name == str_index) {
6825 local_val.used = true;
6826 break;
6827 }
6828 s = local_val.parent;
6829 },
6830 .local_ptr => {
6831 const local_ptr = s.cast(Scope.LocalPtr).?;
6832 if (local_ptr.name == str_index) {
6833 local_ptr.used = true;
6834 break;
6835 }
6836 s = local_ptr.parent;
6837 },
6838 .gen_zir => s = s.cast(GenZir).?.parent,
6839 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
6840 .namespace, .top => break,
6841 };
6842 }
6843 const operand = try gz.addStrTok(.decl_ref, str_index, ident_token);
68446826 outputs[i] = .{
68456827 .name = name,
68466828 .constraint = constraint,
6847 .operand = operand,
6829 .operand = try localVarRef(gz, scope, rl, node, ident_token),
68486830 };
68496831 }
68506832 }
......@@ -6860,7 +6842,7 @@ fn asmExpr(
68606842 const name = try astgen.identAsString(symbolic_name);
68616843 const constraint_token = symbolic_name + 2;
68626844 const constraint = (try astgen.strLitAsString(constraint_token)).index;
6863 const operand = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input_node].lhs);
6845 const operand = try expr(gz, scope, .none, node_datas[input_node].lhs);
68646846 inputs[i] = .{
68656847 .name = name,
68666848 .constraint = constraint,
src/Sema.zig+13-2
......@@ -10253,6 +10253,11 @@ fn zirAsm(
1025310253 const inputs_len = @truncate(u5, extended.small >> 5);
1025410254 const clobbers_len = @truncate(u5, extended.small >> 10);
1025510255 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
10256 const is_global_assembly = sema.func == null;
10257
10258 if (block.is_comptime and !is_global_assembly) {
10259 try sema.requireRuntimeBlock(block, src);
10260 }
1025610261
1025710262 if (extra.data.asm_source == 0) {
1025810263 // This can move to become an AstGen error after inline assembly improvements land
......@@ -10299,7 +10304,14 @@ fn zirAsm(
1029910304 const name = sema.code.nullTerminatedString(input.data.name);
1030010305 _ = name; // TODO: use the name
1030110306
10302 arg.* = sema.resolveInst(input.data.operand);
10307 const uncasted_arg = sema.resolveInst(input.data.operand);
10308 const uncasted_arg_ty = sema.typeOf(uncasted_arg);
10309 switch (uncasted_arg_ty.zigTypeTag()) {
10310 .ComptimeInt => arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted_arg, src),
10311 .ComptimeFloat => arg.* = try sema.coerce(block, Type.initTag(.f64), uncasted_arg, src),
10312 else => arg.* = uncasted_arg,
10313 }
10314
1030310315 const constraint = sema.code.nullTerminatedString(input.data.constraint);
1030410316 needed_capacity += constraint.len / 4 + 1;
1030510317 inputs[arg_i] = constraint;
......@@ -10317,7 +10329,6 @@ fn zirAsm(
1031710329 needed_capacity += (asm_source.len + 3) / 4;
1031810330
1031910331 const gpa = sema.gpa;
10320 try sema.requireRuntimeBlock(block, src);
1032110332 try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
1032210333 const asm_air = try block.addInst(.{
1032310334 .tag = .assembly,
src/codegen/c.zig+13-5
......@@ -3014,9 +3014,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
30143014 } else null;
30153015
30163016 const writer = f.object.writer();
3017 const inputs_extra_begin = extra_i;
3017 try writer.writeAll("{\n");
30183018
3019 for (inputs) |input| {
3019 const inputs_extra_begin = extra_i;
3020 for (inputs) |input, i| {
30203021 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
30213022 // This equation accounts for the fact that even if we have exactly 4 bytes
30223023 // for the string, we still use the next u32 for the null terminator.
......@@ -3032,7 +3033,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
30323033 try f.writeCValue(writer, arg_c_value);
30333034 try writer.writeAll(";\n");
30343035 } else {
3035 return f.fail("TODO non-explicit inline asm regs", .{});
3036 try writer.writeAll("register ");
3037 try f.renderType(writer, f.air.typeOf(input));
3038 try writer.print(" input_{d} = ", .{i});
3039 try f.writeCValue(writer, try f.resolveInst(input));
3040 try writer.writeAll(";\n");
30363041 }
30373042 }
30383043
......@@ -3074,12 +3079,15 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
30743079 }
30753080 try writer.print("\"r\"({s}_constant)", .{reg});
30763081 } else {
3077 // This is blocked by the earlier test
3078 unreachable;
3082 if (index > 0) {
3083 try writer.writeAll(", ");
3084 }
3085 try writer.print("\"r\"(input_{d})", .{index});
30793086 }
30803087 }
30813088 }
30823089 try writer.writeAll(");\n");
3090 try writer.writeAll("}\n");
30833091
30843092 if (f.liveness.isUnused(inst))
30853093 return CValue.none;
test/behavior.zig+1-4
......@@ -165,10 +165,7 @@ test {
165165 }
166166
167167 if (builtin.os.tag != .wasi) {
168 if (builtin.zig_backend == .stage1) {
169 // TODO get these tests passing with stage2
170 _ = @import("behavior/asm.zig");
171 }
168 _ = @import("behavior/asm.zig");
172169 }
173170
174171 if (builtin.zig_backend != .stage2_arm and
test/behavior/asm.zig+37-1
......@@ -5,7 +5,10 @@ const expect = std.testing.expect;
55const is_x86_64_linux = builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux;
66
77comptime {
8 if (is_x86_64_linux) {
8 if (builtin.zig_backend != .stage2_arm and
9 builtin.zig_backend != .stage2_aarch64 and
10 is_x86_64_linux)
11 {
912 asm (
1013 \\.globl this_is_my_alias;
1114 \\.type this_is_my_alias, @function;
......@@ -15,12 +18,26 @@ comptime {
1518}
1619
1720test "module level assembly" {
21 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
22 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
27
1828 if (is_x86_64_linux) {
1929 try expect(this_is_my_alias() == 1234);
2030 }
2131}
2232
2333test "output constraint modifiers" {
34 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
35 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
36 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
37 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
38 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
39 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
40
2441 // This is only testing compilation.
2542 var a: u32 = 3;
2643 asm volatile (""
......@@ -36,6 +53,13 @@ test "output constraint modifiers" {
3653}
3754
3855test "alternative constraints" {
56 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
57 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
59 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
60 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
61 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
62
3963 // Make sure we allow commas as a separator for alternative constraints.
4064 var a: u32 = 3;
4165 asm volatile (""
......@@ -46,6 +70,11 @@ test "alternative constraints" {
4670}
4771
4872test "sized integer/float in asm input" {
73 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
74 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
75 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
77
4978 asm volatile (""
5079 :
5180 : [_] "m" (@as(usize, 3)),
......@@ -89,6 +118,13 @@ test "sized integer/float in asm input" {
89118}
90119
91120test "struct/array/union types as input values" {
121 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
122 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
123 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
124 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
125 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
126 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
127
92128 asm volatile (""
93129 :
94130 : [_] "m" (@as([1]u32, undefined)),