| author | |
| committer | |
| log | 610d604f5b4be220ea0f404af871c4365026a6a7 |
| tree | 56ddbf87ab181c61af498280aa25614bde54493e |
| parent | 53dcf78a25b47aa28d271c4c520eafb7c3474704 |
| parent | e1216077f0edf0efa0b322c5469e2ab572213271 |
| signature |
cbe: use memcpy for underaligned loads and stores9 files changed, 61 insertions(+), 34 deletions(-)
src/codegen/c.zig+61-19| ... | ... | @@ -3017,30 +3017,31 @@ fn airArg(f: *Function) CValue { |
| 3017 | 3017 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3018 | 3018 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 3019 | 3019 | const ptr_info = f.air.typeOf(ty_op.operand).ptrInfo().data; |
| 3020 | const src_ty = ptr_info.pointee_type; | |
| 3020 | 3021 | |
| 3021 | const inst_ty = f.air.typeOfIndex(inst); | |
| 3022 | if (!inst_ty.hasRuntimeBitsIgnoreComptime() or | |
| 3022 | if (!src_ty.hasRuntimeBitsIgnoreComptime() or | |
| 3023 | 3023 | !ptr_info.@"volatile" and f.liveness.isUnused(inst)) |
| 3024 | 3024 | return CValue.none; |
| 3025 | 3025 | |
| 3026 | 3026 | const target = f.object.dg.module.getTarget(); |
| 3027 | const is_array = lowersToArray(inst_ty, target); | |
| 3027 | const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target); | |
| 3028 | const is_array = lowersToArray(src_ty, target); | |
| 3029 | const need_memcpy = !is_aligned or is_array; | |
| 3028 | 3030 | const operand = try f.resolveInst(ty_op.operand); |
| 3029 | 3031 | const writer = f.object.writer(); |
| 3030 | 3032 | |
| 3031 | // We need to separately initialize arrays with a memcpy so they must be mutable. | |
| 3032 | const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); | |
| 3033 | // We need to initialize arrays and unaligned loads with a memcpy so they must be mutable. | |
| 3034 | const local = try f.allocLocal(src_ty, if (need_memcpy) .Mut else .Const); | |
| 3033 | 3035 | |
| 3034 | if (is_array) { | |
| 3035 | // Insert a memcpy to initialize this array. The source operand is always a pointer | |
| 3036 | // and thus we only need to know size/type information from the local type/dest. | |
| 3036 | if (need_memcpy) { | |
| 3037 | 3037 | try writer.writeAll(";\n"); |
| 3038 | 3038 | try writer.writeAll("memcpy("); |
| 3039 | if (!is_array) try writer.writeByte('&'); | |
| 3039 | 3040 | try f.writeCValue(writer, local, .FunctionArgument); |
| 3040 | try writer.writeAll(", "); | |
| 3041 | try f.writeCValue(writer, operand, .FunctionArgument); | |
| 3041 | try writer.writeAll(", (const char *)"); | |
| 3042 | try f.writeCValue(writer, operand, .Other); | |
| 3042 | 3043 | try writer.writeAll(", sizeof("); |
| 3043 | try f.renderTypecast(writer, inst_ty); | |
| 3044 | try f.renderTypecast(writer, src_ty); | |
| 3044 | 3045 | try writer.writeAll("))"); |
| 3045 | 3046 | } else if (ptr_info.host_size != 0) { |
| 3046 | 3047 | var host_pl = Type.Payload.Bits{ |
| ... | ... | @@ -3063,12 +3064,12 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3063 | 3064 | |
| 3064 | 3065 | var field_pl = Type.Payload.Bits{ |
| 3065 | 3066 | .base = .{ .tag = .int_unsigned }, |
| 3066 | .data = @intCast(u16, inst_ty.bitSize(target)), | |
| 3067 | .data = @intCast(u16, src_ty.bitSize(target)), | |
| 3067 | 3068 | }; |
| 3068 | 3069 | const field_ty = Type.initPayload(&field_pl.base); |
| 3069 | 3070 | |
| 3070 | 3071 | try writer.writeAll(" = ("); |
| 3071 | try f.renderTypecast(writer, inst_ty); | |
| 3072 | try f.renderTypecast(writer, src_ty); | |
| 3072 | 3073 | try writer.writeAll(")zig_wrap_"); |
| 3073 | 3074 | try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty); |
| 3074 | 3075 | try writer.writeAll("(("); |
| ... | ... | @@ -3244,8 +3245,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3244 | 3245 | return try airStoreUndefined(f, ptr_info.pointee_type, ptr_val); |
| 3245 | 3246 | |
| 3246 | 3247 | const target = f.object.dg.module.getTarget(); |
| 3248 | const is_aligned = ptr_info.@"align" == 0 or | |
| 3249 | ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target); | |
| 3250 | const is_array = lowersToArray(ptr_info.pointee_type, target); | |
| 3251 | const need_memcpy = !is_aligned or is_array; | |
| 3247 | 3252 | const writer = f.object.writer(); |
| 3248 | if (lowersToArray(ptr_info.pointee_type, target)) { | |
| 3253 | ||
| 3254 | if (need_memcpy) { | |
| 3249 | 3255 | // For this memcpy to safely work we need the rhs to have the same |
| 3250 | 3256 | // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). |
| 3251 | 3257 | assert(src_ty.eql(ptr_info.pointee_type, f.object.dg.module)); |
| ... | ... | @@ -3262,9 +3268,10 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3262 | 3268 | break :blk new_local; |
| 3263 | 3269 | } else src_val; |
| 3264 | 3270 | |
| 3265 | try writer.writeAll("memcpy("); | |
| 3271 | try writer.writeAll("memcpy((char *)"); | |
| 3266 | 3272 | try f.writeCValue(writer, ptr_val, .FunctionArgument); |
| 3267 | 3273 | try writer.writeAll(", "); |
| 3274 | if (!is_array) try writer.writeByte('&'); | |
| 3268 | 3275 | try f.writeCValue(writer, array_src, .FunctionArgument); |
| 3269 | 3276 | try writer.writeAll(", sizeof("); |
| 3270 | 3277 | try f.renderTypecast(writer, src_ty); |
| ... | ... | @@ -4186,11 +4193,46 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4186 | 4193 | extra_i += clobber.len / 4 + 1; |
| 4187 | 4194 | } |
| 4188 | 4195 | } |
| 4189 | const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 4196 | { | |
| 4197 | const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 4198 | ||
| 4199 | var stack = std.heap.stackFallback(256, f.object.dg.gpa); | |
| 4200 | const allocator = stack.get(); | |
| 4201 | const fixed_asm_source = try allocator.alloc(u8, asm_source.len); | |
| 4202 | defer allocator.free(fixed_asm_source); | |
| 4203 | ||
| 4204 | var src_i: usize = 0; | |
| 4205 | var dst_i: usize = 0; | |
| 4206 | while (src_i < asm_source.len) : (src_i += 1) { | |
| 4207 | fixed_asm_source[dst_i] = asm_source[src_i]; | |
| 4208 | dst_i += 1; | |
| 4209 | if (asm_source[src_i] != '%' or src_i + 1 >= asm_source.len) continue; | |
| 4210 | src_i += 1; | |
| 4211 | if (asm_source[src_i] != '[') { | |
| 4212 | // This handles %% | |
| 4213 | fixed_asm_source[dst_i] = asm_source[src_i]; | |
| 4214 | dst_i += 1; | |
| 4215 | continue; | |
| 4216 | } | |
| 4217 | const len = std.mem.indexOfScalar(u8, asm_source[src_i + 1 ..], ']') orelse | |
| 4218 | return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); | |
| 4219 | if (std.mem.indexOfScalar(u8, asm_source[src_i + 1 ..][0..len], ':')) |colon| { | |
| 4220 | const modifier = asm_source[src_i + 1 + colon + 1 .. src_i + 1 + len]; | |
| 4221 | std.mem.copy(u8, fixed_asm_source[dst_i..], modifier); | |
| 4222 | dst_i += modifier.len; | |
| 4223 | ||
| 4224 | const name = asm_source[src_i .. src_i + 1 + colon]; | |
| 4225 | std.mem.copy(u8, fixed_asm_source[dst_i..], name); | |
| 4226 | dst_i += name.len; | |
| 4227 | ||
| 4228 | src_i += len; | |
| 4229 | } | |
| 4230 | } | |
| 4190 | 4231 | |
| 4191 | try writer.writeAll("__asm"); | |
| 4192 | if (is_volatile) try writer.writeAll(" volatile"); | |
| 4193 | try writer.print("({s}", .{fmtStringLiteral(asm_source)}); | |
| 4232 | try writer.writeAll("__asm"); | |
| 4233 | if (is_volatile) try writer.writeAll(" volatile"); | |
| 4234 | try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])}); | |
| 4235 | } | |
| 4194 | 4236 | |
| 4195 | 4237 | extra_i = constraints_extra_begin; |
| 4196 | 4238 | var locals_index = locals_begin; |
test/behavior/asm.zig-1| ... | ... | @@ -140,7 +140,6 @@ export fn derp() i32 { |
| 140 | 140 | test "asm modifiers (AArch64)" { |
| 141 | 141 | if (builtin.target.cpu.arch != .aarch64) return error.SkipZigTest; |
| 142 | 142 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 143 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 144 | 143 | |
| 145 | 144 | var x: u32 = 15; |
| 146 | 145 | const double = asm ("add %[ret:w], %[in:w], %[in:w]" |
test/behavior/basic.zig-2| ... | ... | @@ -754,7 +754,6 @@ fn maybe(x: bool) anyerror!?u32 { |
| 754 | 754 | } |
| 755 | 755 | |
| 756 | 756 | test "pointer to thread local array" { |
| 757 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 758 | 757 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 759 | 758 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 760 | 759 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | ... | @@ -770,7 +769,6 @@ threadlocal var buffer: [11]u8 = undefined; |
| 770 | 769 | |
| 771 | 770 | test "auto created variables have correct alignment" { |
| 772 | 771 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 773 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 774 | 772 | |
| 775 | 773 | const S = struct { |
| 776 | 774 | fn foo(str: [*]const u8) u32 { |
test/behavior/bitreverse.zig-1| ... | ... | @@ -155,7 +155,6 @@ fn vector0() !void { |
| 155 | 155 | test "bitReverse vectors u0" { |
| 156 | 156 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 157 | 157 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 158 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 159 | 158 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 160 | 159 | |
| 161 | 160 | comptime try vector0(); |
test/behavior/byteswap.zig-1| ... | ... | @@ -121,7 +121,6 @@ fn vector0() !void { |
| 121 | 121 | test "@byteSwap vectors u0" { |
| 122 | 122 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 123 | 123 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 124 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 125 | 124 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 126 | 125 | |
| 127 | 126 | comptime try vector0(); |
test/behavior/math.zig-1| ... | ... | @@ -1518,7 +1518,6 @@ fn testRound(comptime T: type, x: T) !void { |
| 1518 | 1518 | } |
| 1519 | 1519 | |
| 1520 | 1520 | test "vector integer addition" { |
| 1521 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 1522 | 1521 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1523 | 1522 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1524 | 1523 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
test/behavior/ptrcast.zig-1| ... | ... | @@ -78,7 +78,6 @@ fn testReinterpretBytesAsExternStruct() !void { |
| 78 | 78 | |
| 79 | 79 | test "reinterpret bytes of an extern struct (with under-aligned fields) into another" { |
| 80 | 80 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 81 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: Under-aligned fields are not yet supported in the CBE | |
| 82 | 81 | |
| 83 | 82 | try testReinterpretExternStructAsExternStruct(); |
| 84 | 83 | comptime try testReinterpretExternStructAsExternStruct(); |
test/behavior/struct.zig-1| ... | ... | @@ -641,7 +641,6 @@ test "default struct initialization fields" { |
| 641 | 641 | |
| 642 | 642 | test "packed array 24bits" { |
| 643 | 643 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 644 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 645 | 644 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 646 | 645 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 647 | 646 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
test/behavior/vector.zig-7| ... | ... | @@ -75,7 +75,6 @@ test "vector bin compares with mem.eql" { |
| 75 | 75 | |
| 76 | 76 | test "vector int operators" { |
| 77 | 77 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 78 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 79 | 78 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 80 | 79 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 81 | 80 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -139,7 +138,6 @@ test "vector bit operators" { |
| 139 | 138 | |
| 140 | 139 | test "implicit cast vector to array" { |
| 141 | 140 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 142 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 143 | 141 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 144 | 142 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 145 | 143 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -158,7 +156,6 @@ test "implicit cast vector to array" { |
| 158 | 156 | |
| 159 | 157 | test "array to vector" { |
| 160 | 158 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 161 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 162 | 159 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 163 | 160 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 164 | 161 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -177,7 +174,6 @@ test "array to vector" { |
| 177 | 174 | |
| 178 | 175 | test "tuple to vector" { |
| 179 | 176 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 180 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 181 | 177 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 182 | 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 183 | 179 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -210,7 +206,6 @@ test "tuple to vector" { |
| 210 | 206 | |
| 211 | 207 | test "vector casts of sizes not divisible by 8" { |
| 212 | 208 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 213 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 214 | 209 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 215 | 210 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 216 | 211 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -945,7 +940,6 @@ test "multiplication-assignment operator with an array operand" { |
| 945 | 940 | return error.SkipZigTest; |
| 946 | 941 | } |
| 947 | 942 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 948 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 949 | 943 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 950 | 944 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 951 | 945 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -1194,7 +1188,6 @@ test "zero divisor" { |
| 1194 | 1188 | |
| 1195 | 1189 | test "zero multiplicand" { |
| 1196 | 1190 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1197 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 1198 | 1191 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1199 | 1192 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1200 | 1193 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |