| author | |
| committer | |
| log | 3c64c519e65861868e05c47de2d8605a579edf06 |
| tree | 9e06748f9c048b4f54982db9163f6e46e8eb9746 |
| parent | d227f76afbacb619152ffd943cf3018e62e00adc |
| parent | 907dc1e13f657f349dbdecf739b2f1a13ad7011a |
| signature |
CBE: improve support for asm inputs5 files changed, 81 insertions(+), 47 deletions(-)
src/AstGen.zig+17-35| ... | ... | @@ -6423,7 +6423,6 @@ fn identifier( |
| 6423 | 6423 | |
| 6424 | 6424 | const astgen = gz.astgen; |
| 6425 | 6425 | const tree = astgen.tree; |
| 6426 | const gpa = astgen.gpa; | |
| 6427 | 6426 | const main_tokens = tree.nodes.items(.main_token); |
| 6428 | 6427 | |
| 6429 | 6428 | const ident_token = main_tokens[ident]; |
| ... | ... | @@ -6467,6 +6466,19 @@ fn identifier( |
| 6467 | 6466 | } |
| 6468 | 6467 | |
| 6469 | 6468 | // Local variables, including function parameters. |
| 6469 | return localVarRef(gz, scope, rl, ident, ident_token); | |
| 6470 | } | |
| 6471 | ||
| 6472 | fn 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 | ||
| 6470 | 6482 | const name_str_index = try astgen.identAsString(ident_token); |
| 6471 | 6483 | var s = scope; |
| 6472 | 6484 | var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already |
| ... | ... | @@ -6808,43 +6820,13 @@ fn asmExpr( |
| 6808 | 6820 | }; |
| 6809 | 6821 | } else { |
| 6810 | 6822 | 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? | |
| 6814 | 6825 | // 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); | |
| 6844 | 6826 | outputs[i] = .{ |
| 6845 | 6827 | .name = name, |
| 6846 | 6828 | .constraint = constraint, |
| 6847 | .operand = operand, | |
| 6829 | .operand = try localVarRef(gz, scope, rl, node, ident_token), | |
| 6848 | 6830 | }; |
| 6849 | 6831 | } |
| 6850 | 6832 | } |
| ... | ... | @@ -6860,7 +6842,7 @@ fn asmExpr( |
| 6860 | 6842 | const name = try astgen.identAsString(symbolic_name); |
| 6861 | 6843 | const constraint_token = symbolic_name + 2; |
| 6862 | 6844 | 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); | |
| 6864 | 6846 | inputs[i] = .{ |
| 6865 | 6847 | .name = name, |
| 6866 | 6848 | .constraint = constraint, |
src/Sema.zig+13-2| ... | ... | @@ -10253,6 +10253,11 @@ fn zirAsm( |
| 10253 | 10253 | const inputs_len = @truncate(u5, extended.small >> 5); |
| 10254 | 10254 | const clobbers_len = @truncate(u5, extended.small >> 10); |
| 10255 | 10255 | 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 | } | |
| 10256 | 10261 | |
| 10257 | 10262 | if (extra.data.asm_source == 0) { |
| 10258 | 10263 | // This can move to become an AstGen error after inline assembly improvements land |
| ... | ... | @@ -10299,7 +10304,14 @@ fn zirAsm( |
| 10299 | 10304 | const name = sema.code.nullTerminatedString(input.data.name); |
| 10300 | 10305 | _ = name; // TODO: use the name |
| 10301 | 10306 | |
| 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 | ||
| 10303 | 10315 | const constraint = sema.code.nullTerminatedString(input.data.constraint); |
| 10304 | 10316 | needed_capacity += constraint.len / 4 + 1; |
| 10305 | 10317 | inputs[arg_i] = constraint; |
| ... | ... | @@ -10317,7 +10329,6 @@ fn zirAsm( |
| 10317 | 10329 | needed_capacity += (asm_source.len + 3) / 4; |
| 10318 | 10330 | |
| 10319 | 10331 | const gpa = sema.gpa; |
| 10320 | try sema.requireRuntimeBlock(block, src); | |
| 10321 | 10332 | try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); |
| 10322 | 10333 | const asm_air = try block.addInst(.{ |
| 10323 | 10334 | .tag = .assembly, |
src/codegen/c.zig+13-5| ... | ... | @@ -3014,9 +3014,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3014 | 3014 | } else null; |
| 3015 | 3015 | |
| 3016 | 3016 | const writer = f.object.writer(); |
| 3017 | const inputs_extra_begin = extra_i; | |
| 3017 | try writer.writeAll("{\n"); | |
| 3018 | 3018 | |
| 3019 | for (inputs) |input| { | |
| 3019 | const inputs_extra_begin = extra_i; | |
| 3020 | for (inputs) |input, i| { | |
| 3020 | 3021 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); |
| 3021 | 3022 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 3022 | 3023 | // 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 { |
| 3032 | 3033 | try f.writeCValue(writer, arg_c_value); |
| 3033 | 3034 | try writer.writeAll(";\n"); |
| 3034 | 3035 | } 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"); | |
| 3036 | 3041 | } |
| 3037 | 3042 | } |
| 3038 | 3043 | |
| ... | ... | @@ -3074,12 +3079,15 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3074 | 3079 | } |
| 3075 | 3080 | try writer.print("\"r\"({s}_constant)", .{reg}); |
| 3076 | 3081 | } 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}); | |
| 3079 | 3086 | } |
| 3080 | 3087 | } |
| 3081 | 3088 | } |
| 3082 | 3089 | try writer.writeAll(");\n"); |
| 3090 | try writer.writeAll("}\n"); | |
| 3083 | 3091 | |
| 3084 | 3092 | if (f.liveness.isUnused(inst)) |
| 3085 | 3093 | return CValue.none; |
test/behavior.zig+1-4| ... | ... | @@ -165,10 +165,7 @@ test { |
| 165 | 165 | } |
| 166 | 166 | |
| 167 | 167 | 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"); | |
| 172 | 169 | } |
| 173 | 170 | |
| 174 | 171 | if (builtin.zig_backend != .stage2_arm and |
test/behavior/asm.zig+37-1| ... | ... | @@ -5,7 +5,10 @@ const expect = std.testing.expect; |
| 5 | 5 | const is_x86_64_linux = builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux; |
| 6 | 6 | |
| 7 | 7 | comptime { |
| 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 | { | |
| 9 | 12 | asm ( |
| 10 | 13 | \\.globl this_is_my_alias; |
| 11 | 14 | \\.type this_is_my_alias, @function; |
| ... | ... | @@ -15,12 +18,26 @@ comptime { |
| 15 | 18 | } |
| 16 | 19 | |
| 17 | 20 | test "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 | ||
| 18 | 28 | if (is_x86_64_linux) { |
| 19 | 29 | try expect(this_is_my_alias() == 1234); |
| 20 | 30 | } |
| 21 | 31 | } |
| 22 | 32 | |
| 23 | 33 | test "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 | ||
| 24 | 41 | // This is only testing compilation. |
| 25 | 42 | var a: u32 = 3; |
| 26 | 43 | asm volatile ("" |
| ... | ... | @@ -36,6 +53,13 @@ test "output constraint modifiers" { |
| 36 | 53 | } |
| 37 | 54 | |
| 38 | 55 | test "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 | ||
| 39 | 63 | // Make sure we allow commas as a separator for alternative constraints. |
| 40 | 64 | var a: u32 = 3; |
| 41 | 65 | asm volatile ("" |
| ... | ... | @@ -46,6 +70,11 @@ test "alternative constraints" { |
| 46 | 70 | } |
| 47 | 71 | |
| 48 | 72 | test "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 | ||
| 49 | 78 | asm volatile ("" |
| 50 | 79 | : |
| 51 | 80 | : [_] "m" (@as(usize, 3)), |
| ... | ... | @@ -89,6 +118,13 @@ test "sized integer/float in asm input" { |
| 89 | 118 | } |
| 90 | 119 | |
| 91 | 120 | test "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 | ||
| 92 | 128 | asm volatile ("" |
| 93 | 129 | : |
| 94 | 130 | : [_] "m" (@as([1]u32, undefined)), |