| ... | @@ -19,6 +19,7 @@ const Zir = @import("../Zir.zig"); | ... | @@ -19,6 +19,7 @@ const Zir = @import("../Zir.zig"); |
| 19 | const Liveness = @import("../Liveness.zig"); | 19 | const Liveness = @import("../Liveness.zig"); |
| 20 | | 20 | |
| 21 | const Mutability = enum { Const, Mut }; | 21 | const Mutability = enum { Const, Mut }; |
| | 22 | const BigIntConst = std.math.big.int.Const; |
| 22 | | 23 | |
| 23 | pub const CValue = union(enum) { | 24 | pub const CValue = union(enum) { |
| 24 | none: void, | 25 | none: void, |
| ... | @@ -226,13 +227,59 @@ pub const DeclGen = struct { | ... | @@ -226,13 +227,59 @@ pub const DeclGen = struct { |
| 226 | try dg.renderDeclName(decl, writer); | 227 | try dg.renderDeclName(decl, writer); |
| 227 | } | 228 | } |
| 228 | | 229 | |
| | 230 | fn renderInt128( |
| | 231 | writer: anytype, |
| | 232 | int_val: anytype, |
| | 233 | ) error{ OutOfMemory, AnalysisFail }!void { |
| | 234 | const int_info = @typeInfo(@TypeOf(int_val)).Int; |
| | 235 | const is_signed = int_info.signedness == .signed; |
| | 236 | const is_neg = int_val < 0; |
| | 237 | comptime assert(int_info.bits > 64 and int_info.bits <= 128); |
| | 238 | |
| | 239 | // Clang and GCC don't support 128-bit integer constants but will hopefully unfold them |
| | 240 | // if we construct one manually. |
| | 241 | const magnitude = std.math.absCast(int_val); |
| | 242 | |
| | 243 | const high = @truncate(u64, magnitude >> 64); |
| | 244 | const low = @truncate(u64, magnitude); |
| | 245 | |
| | 246 | // (int128_t)/<->( ( (uint128_t)( val_high << 64 )u ) + (uint128_t)val_low/u ) |
| | 247 | if (is_signed) try writer.writeAll("(int128_t)"); |
| | 248 | if (is_neg) try writer.writeByte('-'); |
| | 249 | |
| | 250 | assert(high > 0); |
| | 251 | try writer.print("(((uint128_t)0x{x}u<<64)", .{high}); |
| | 252 | |
| | 253 | if (low > 0) |
| | 254 | try writer.print("+(uint128_t)0x{x}u", .{low}); |
| | 255 | |
| | 256 | return writer.writeByte(')'); |
| | 257 | } |
| | 258 | |
| | 259 | fn renderBigIntConst( |
| | 260 | dg: *DeclGen, |
| | 261 | writer: anytype, |
| | 262 | val: BigIntConst, |
| | 263 | signed: bool, |
| | 264 | ) error{ OutOfMemory, AnalysisFail }!void { |
| | 265 | if (signed) { |
| | 266 | try renderInt128(writer, val.to(i128) catch { |
| | 267 | return dg.fail("TODO implement integer constants larger than 128 bits", .{}); |
| | 268 | }); |
| | 269 | } else { |
| | 270 | try renderInt128(writer, val.to(u128) catch { |
| | 271 | return dg.fail("TODO implement integer constants larger than 128 bits", .{}); |
| | 272 | }); |
| | 273 | } |
| | 274 | } |
| | 275 | |
| 229 | fn renderValue( | 276 | fn renderValue( |
| 230 | dg: *DeclGen, | 277 | dg: *DeclGen, |
| 231 | writer: anytype, | 278 | writer: anytype, |
| 232 | ty: Type, | 279 | ty: Type, |
| 233 | val: Value, | 280 | val: Value, |
| 234 | ) error{ OutOfMemory, AnalysisFail }!void { | 281 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 235 | if (val.isUndef()) { | 282 | if (val.isUndefDeep()) { |
| 236 | switch (ty.zigTypeTag()) { | 283 | switch (ty.zigTypeTag()) { |
| 237 | // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) | 284 | // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) |
| 238 | // with 'error: expected expression' (including when built with 'zig cc') | 285 | // with 'error: expected expression' (including when built with 'zig cc') |
| ... | @@ -240,18 +287,18 @@ pub const DeclGen = struct { | ... | @@ -240,18 +287,18 @@ pub const DeclGen = struct { |
| 240 | const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse | 287 | const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse |
| 241 | return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); | 288 | return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 242 | switch (c_bits) { | 289 | switch (c_bits) { |
| 243 | 8 => return writer.writeAll("0xaaU"), | 290 | 8 => return writer.writeAll("0xaau"), |
| 244 | 16 => return writer.writeAll("0xaaaaU"), | 291 | 16 => return writer.writeAll("0xaaaau"), |
| 245 | 32 => return writer.writeAll("0xaaaaaaaaU"), | 292 | 32 => return writer.writeAll("0xaaaaaaaau"), |
| 246 | 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaaUL"), | 293 | 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaau"), |
| 247 | 128 => return writer.writeAll("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaULL"), | 294 | 128 => return renderInt128(writer, @as(u128, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)), |
| 248 | else => unreachable, | 295 | else => unreachable, |
| 249 | } | 296 | } |
| 250 | }, | 297 | }, |
| 251 | .Float => { | 298 | .Float => { |
| 252 | switch (ty.floatBits(dg.module.getTarget())) { | 299 | switch (ty.floatBits(dg.module.getTarget())) { |
| 253 | 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaa)"), | 300 | 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaau)"), |
| 254 | 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaa)"), | 301 | 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaau)"), |
| 255 | else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), | 302 | else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), |
| 256 | } | 303 | } |
| 257 | }, | 304 | }, |
| ... | @@ -265,10 +312,14 @@ pub const DeclGen = struct { | ... | @@ -265,10 +312,14 @@ pub const DeclGen = struct { |
| 265 | } | 312 | } |
| 266 | } | 313 | } |
| 267 | switch (ty.zigTypeTag()) { | 314 | switch (ty.zigTypeTag()) { |
| 268 | .Int => { | 315 | .Int => switch (val.tag()) { |
| 269 | if (ty.isSignedInt()) | 316 | .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()), |
| 270 | return writer.print("{d}", .{val.toSignedInt()}); | 317 | .int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true), |
| 271 | return writer.print("{d}", .{val.toUnsignedInt()}); | 318 | else => { |
| | 319 | if (ty.isSignedInt()) |
| | 320 | return writer.print("{d}", .{val.toSignedInt()}); |
| | 321 | return writer.print("{d}u", .{val.toUnsignedInt()}); |
| | 322 | }, |
| 272 | }, | 323 | }, |
| 273 | .Float => { | 324 | .Float => { |
| 274 | if (ty.floatBits(dg.module.getTarget()) <= 64) { | 325 | if (ty.floatBits(dg.module.getTarget()) <= 64) { |
| ... | @@ -286,8 +337,11 @@ pub const DeclGen = struct { | ... | @@ -286,8 +337,11 @@ pub const DeclGen = struct { |
| 286 | return dg.fail("TODO: C backend: implement lowering large float values", .{}); | 337 | return dg.fail("TODO: C backend: implement lowering large float values", .{}); |
| 287 | }, | 338 | }, |
| 288 | .Pointer => switch (val.tag()) { | 339 | .Pointer => switch (val.tag()) { |
| 289 | .null_value, .zero => try writer.writeAll("NULL"), | 340 | .null_value => try writer.writeAll("NULL"), |
| 290 | .one => try writer.writeAll("1"), | 341 | // Technically this should produce NULL but the integer literal 0 will always coerce |
| | 342 | // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc) |
| | 343 | // between pointers and 0, which is an extension to begin with. |
| | 344 | .zero => try writer.writeByte('0'), |
| 291 | .decl_ref => { | 345 | .decl_ref => { |
| 292 | const decl = val.castTag(.decl_ref).?.data; | 346 | const decl = val.castTag(.decl_ref).?.data; |
| 293 | return dg.renderDeclValue(writer, ty, val, decl); | 347 | return dg.renderDeclValue(writer, ty, val, decl); |
| ... | @@ -316,6 +370,11 @@ pub const DeclGen = struct { | ... | @@ -316,6 +370,11 @@ pub const DeclGen = struct { |
| 316 | const decl = val.castTag(.extern_fn).?.data; | 370 | const decl = val.castTag(.extern_fn).?.data; |
| 317 | try dg.renderDeclName(decl, writer); | 371 | try dg.renderDeclName(decl, writer); |
| 318 | }, | 372 | }, |
| | 373 | .int_u64, .one => { |
| | 374 | try writer.writeAll("(("); |
| | 375 | try dg.renderType(writer, ty); |
| | 376 | try writer.print(")0x{x}u)", .{val.toUnsignedInt()}); |
| | 377 | }, |
| 319 | else => unreachable, | 378 | else => unreachable, |
| 320 | }, | 379 | }, |
| 321 | .Array => { | 380 | .Array => { |
| ... | @@ -728,6 +787,8 @@ pub const DeclGen = struct { | ... | @@ -728,6 +787,8 @@ pub const DeclGen = struct { |
| 728 | .i32 => try w.writeAll("int32_t"), | 787 | .i32 => try w.writeAll("int32_t"), |
| 729 | .u64 => try w.writeAll("uint64_t"), | 788 | .u64 => try w.writeAll("uint64_t"), |
| 730 | .i64 => try w.writeAll("int64_t"), | 789 | .i64 => try w.writeAll("int64_t"), |
| | 790 | .u128 => try w.writeAll("uint128_t"), |
| | 791 | .i128 => try w.writeAll("int128_t"), |
| 731 | .usize => try w.writeAll("uintptr_t"), | 792 | .usize => try w.writeAll("uintptr_t"), |
| 732 | .isize => try w.writeAll("intptr_t"), | 793 | .isize => try w.writeAll("intptr_t"), |
| 733 | .c_short => try w.writeAll("short"), | 794 | .c_short => try w.writeAll("short"), |
| ... | @@ -787,8 +848,9 @@ pub const DeclGen = struct { | ... | @@ -787,8 +848,9 @@ pub const DeclGen = struct { |
| 787 | }, | 848 | }, |
| 788 | .Array => { | 849 | .Array => { |
| 789 | // We are referencing the array so it will decay to a C pointer. | 850 | // We are referencing the array so it will decay to a C pointer. |
| 790 | try dg.renderType(w, t.elemType()); | 851 | // NB: arrays are not really types in C so they are either specified in the declaration |
| 791 | return w.writeAll(" *"); | 852 | // or are already pointed to; our only job is to render the element type. |
| | 853 | return dg.renderType(w, t.elemType()); |
| 792 | }, | 854 | }, |
| 793 | .Optional => { | 855 | .Optional => { |
| 794 | var opt_buf: Type.Payload.ElemType = undefined; | 856 | var opt_buf: Type.Payload.ElemType = undefined; |
| ... | @@ -987,7 +1049,7 @@ pub fn genDecl(o: *Object) !void { | ... | @@ -987,7 +1049,7 @@ pub fn genDecl(o: *Object) !void { |
| 987 | } | 1049 | } |
| 988 | try fwd_decl_writer.writeAll(";\n"); | 1050 | try fwd_decl_writer.writeAll(";\n"); |
| 989 | | 1051 | |
| 990 | if (variable.init.isUndef()) { | 1052 | if (variable.init.isUndefDeep()) { |
| 991 | return; | 1053 | return; |
| 992 | } | 1054 | } |
| 993 | | 1055 | |
| ... | @@ -1070,10 +1132,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1070,10 +1132,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1070 | | 1132 | |
| 1071 | // TODO use a different strategy for add that communicates to the optimizer | 1133 | // TODO use a different strategy for add that communicates to the optimizer |
| 1072 | // that wrapping is UB. | 1134 | // that wrapping is UB. |
| 1073 | .add, .ptr_add => try airBinOp (f, inst, " + "), | 1135 | .add => try airBinOp (f, inst, " + "), |
| | 1136 | .ptr_add => try airPtrAddSub (f, inst, " + "), |
| 1074 | // TODO use a different strategy for sub that communicates to the optimizer | 1137 | // TODO use a different strategy for sub that communicates to the optimizer |
| 1075 | // that wrapping is UB. | 1138 | // that wrapping is UB. |
| 1076 | .sub, .ptr_sub => try airBinOp (f, inst, " - "), | 1139 | .sub => try airBinOp (f, inst, " - "), |
| | 1140 | .ptr_sub => try airPtrAddSub (f, inst, " - "), |
| 1077 | // TODO use a different strategy for mul that communicates to the optimizer | 1141 | // TODO use a different strategy for mul that communicates to the optimizer |
| 1078 | // that wrapping is UB. | 1142 | // that wrapping is UB. |
| 1079 | .mul => try airBinOp (f, inst, " * "), | 1143 | .mul => try airBinOp (f, inst, " * "), |
| ... | @@ -1187,7 +1251,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1187,7 +1251,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1187 | .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"), | 1251 | .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"), |
| 1188 | .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"), | 1252 | .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"), |
| 1189 | | 1253 | |
| 1190 | .ptr_elem_val => try airPtrElemVal(f, inst, "["), | 1254 | .ptr_elem_val => try airPtrElemVal(f, inst), |
| 1191 | .ptr_elem_ptr => try airPtrElemPtr(f, inst), | 1255 | .ptr_elem_ptr => try airPtrElemPtr(f, inst), |
| 1192 | .slice_elem_val => try airSliceElemVal(f, inst), | 1256 | .slice_elem_val => try airSliceElemVal(f, inst), |
| 1193 | .slice_elem_ptr => try airSliceElemPtr(f, inst), | 1257 | .slice_elem_ptr => try airSliceElemPtr(f, inst), |
| ... | @@ -1240,20 +1304,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) ! | ... | @@ -1240,20 +1304,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) ! |
| 1240 | return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{}); | 1304 | return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{}); |
| 1241 | } | 1305 | } |
| 1242 | | 1306 | |
| 1243 | fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue { | 1307 | fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1244 | const is_volatile = false; // TODO | 1308 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 1245 | if (!is_volatile and f.liveness.isUnused(inst)) | 1309 | const ptr_ty = f.air.typeOf(bin_op.lhs); |
| 1246 | return CValue.none; | 1310 | if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none; |
| 1247 | | 1311 | |
| 1248 | _ = prefix; | 1312 | const ptr = try f.resolveInst(bin_op.lhs); |
| 1249 | return f.fail("TODO: C backend: airPtrElemVal", .{}); | 1313 | const index = try f.resolveInst(bin_op.rhs); |
| | 1314 | const writer = f.object.writer(); |
| | 1315 | const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); |
| | 1316 | try writer.writeAll(" = "); |
| | 1317 | try f.writeCValue(writer, ptr); |
| | 1318 | try writer.writeByte('['); |
| | 1319 | try f.writeCValue(writer, index); |
| | 1320 | try writer.writeAll("];\n"); |
| | 1321 | return local; |
| 1250 | } | 1322 | } |
| 1251 | | 1323 | |
| 1252 | fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { | 1324 | fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1253 | if (f.liveness.isUnused(inst)) | 1325 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 1254 | return CValue.none; | | |
| 1255 | | 1326 | |
| 1256 | return f.fail("TODO: C backend: airPtrElemPtr", .{}); | 1327 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; |
| | 1328 | const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 1329 | |
| | 1330 | const ptr = try f.resolveInst(bin_op.lhs); |
| | 1331 | const index = try f.resolveInst(bin_op.rhs); |
| | 1332 | const writer = f.object.writer(); |
| | 1333 | const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); |
| | 1334 | try writer.writeAll(" = &"); |
| | 1335 | try f.writeCValue(writer, ptr); |
| | 1336 | try writer.writeByte('['); |
| | 1337 | try f.writeCValue(writer, index); |
| | 1338 | try writer.writeAll("];\n"); |
| | 1339 | return local; |
| 1257 | } | 1340 | } |
| 1258 | | 1341 | |
| 1259 | fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | 1342 | fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -1317,6 +1400,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1317,6 +1400,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1317 | const local = try f.allocLocal(elem_type, mutability); | 1400 | const local = try f.allocLocal(elem_type, mutability); |
| 1318 | try writer.writeAll(";\n"); | 1401 | try writer.writeAll(";\n"); |
| 1319 | | 1402 | |
| | 1403 | // Arrays are already pointers so they don't need to be referenced. |
| | 1404 | if (elem_type.zigTypeTag() == .Array) |
| | 1405 | return CValue{ .local = local.local }; |
| | 1406 | |
| 1320 | return CValue{ .local_ref = local.local }; | 1407 | return CValue{ .local_ref = local.local }; |
| 1321 | } | 1408 | } |
| 1322 | | 1409 | |
| ... | @@ -1344,6 +1431,8 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1344,6 +1431,8 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1344 | if (!is_volatile and f.liveness.isUnused(inst)) | 1431 | if (!is_volatile and f.liveness.isUnused(inst)) |
| 1345 | return CValue.none; | 1432 | return CValue.none; |
| 1346 | const inst_ty = f.air.typeOfIndex(inst); | 1433 | const inst_ty = f.air.typeOfIndex(inst); |
| | 1434 | if (inst_ty.zigTypeTag() == .Array) |
| | 1435 | return f.fail("TODO: C backend: implement airLoad for arrays", .{}); |
| 1347 | const operand = try f.resolveInst(ty_op.operand); | 1436 | const operand = try f.resolveInst(ty_op.operand); |
| 1348 | const writer = f.object.writer(); | 1437 | const writer = f.object.writer(); |
| 1349 | const local = try f.allocLocal(inst_ty, .Const); | 1438 | const local = try f.allocLocal(inst_ty, .Const); |
| ... | @@ -1470,7 +1559,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1470,7 +1559,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1470 | return local; | 1559 | return local; |
| 1471 | } | 1560 | } |
| 1472 | | 1561 | |
| 1473 | fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { | 1562 | fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue { |
| 1474 | const is_debug_build = f.object.dg.module.optimizeMode() == .Debug; | 1563 | const is_debug_build = f.object.dg.module.optimizeMode() == .Debug; |
| 1475 | if (!is_debug_build) | 1564 | if (!is_debug_build) |
| 1476 | return CValue.none; | 1565 | return CValue.none; |
| ... | @@ -1494,9 +1583,11 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { | ... | @@ -1494,9 +1583,11 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { |
| 1494 | try writer.writeAll("));\n"); | 1583 | try writer.writeAll("));\n"); |
| 1495 | }, | 1584 | }, |
| 1496 | else => { | 1585 | else => { |
| | 1586 | const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*"; |
| | 1587 | |
| 1497 | try writer.writeAll("memset("); | 1588 | try writer.writeAll("memset("); |
| 1498 | try f.writeCValue(writer, dest_ptr); | 1589 | try f.writeCValue(writer, dest_ptr); |
| 1499 | try writer.writeAll(", 0xaa, sizeof(*"); | 1590 | try writer.print(", 0xaa, sizeof({s}", .{indirection}); |
| 1500 | try f.writeCValue(writer, dest_ptr); | 1591 | try f.writeCValue(writer, dest_ptr); |
| 1501 | try writer.writeAll("));\n"); | 1592 | try writer.writeAll("));\n"); |
| 1502 | }, | 1593 | }, |
| ... | @@ -1509,11 +1600,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1509,11 +1600,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1509 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 1600 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 1510 | const dest_ptr = try f.resolveInst(bin_op.lhs); | 1601 | const dest_ptr = try f.resolveInst(bin_op.lhs); |
| 1511 | const src_val = try f.resolveInst(bin_op.rhs); | 1602 | const src_val = try f.resolveInst(bin_op.rhs); |
| | 1603 | const lhs_type = f.air.typeOf(bin_op.lhs); |
| 1512 | | 1604 | |
| | 1605 | // TODO Sema should emit a different instruction when the store should |
| | 1606 | // possibly do the safety 0xaa bytes for undefined. |
| 1513 | const src_val_is_undefined = | 1607 | const src_val_is_undefined = |
| 1514 | if (f.air.value(bin_op.rhs)) |v| v.isUndef() else false; | 1608 | if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; |
| 1515 | if (src_val_is_undefined) | 1609 | if (src_val_is_undefined) |
| 1516 | return try airStoreUndefined(f, dest_ptr); | 1610 | return try airStoreUndefined(f, dest_ptr, lhs_type); |
| | 1611 | |
| | 1612 | // Don't check this for airStoreUndefined as that will work for arrays already |
| | 1613 | if (lhs_type.childType().zigTypeTag() == .Array) |
| | 1614 | return f.fail("TODO: C backend: implement airStore for arrays", .{}); |
| 1517 | | 1615 | |
| 1518 | const writer = f.object.writer(); | 1616 | const writer = f.object.writer(); |
| 1519 | switch (dest_ptr) { | 1617 | switch (dest_ptr) { |
| ... | @@ -1810,6 +1908,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue | ... | @@ -1810,6 +1908,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue |
| 1810 | return local; | 1908 | return local; |
| 1811 | } | 1909 | } |
| 1812 | | 1910 | |
| | 1911 | fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { |
| | 1912 | if (f.liveness.isUnused(inst)) |
| | 1913 | return CValue.none; |
| | 1914 | |
| | 1915 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| | 1916 | const lhs = try f.resolveInst(bin_op.lhs); |
| | 1917 | const rhs = try f.resolveInst(bin_op.rhs); |
| | 1918 | |
| | 1919 | const writer = f.object.writer(); |
| | 1920 | const inst_ty = f.air.typeOfIndex(inst); |
| | 1921 | const local = try f.allocLocal(inst_ty, .Const); |
| | 1922 | |
| | 1923 | // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer, |
| | 1924 | // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced. |
| | 1925 | try writer.writeAll(" = ("); |
| | 1926 | try f.renderType(writer, inst_ty); |
| | 1927 | try writer.writeAll(")(((uintptr_t)"); |
| | 1928 | try f.writeCValue(writer, lhs); |
| | 1929 | try writer.print("){s}(", .{operator}); |
| | 1930 | try f.writeCValue(writer, rhs); |
| | 1931 | try writer.writeAll("*sizeof("); |
| | 1932 | try f.renderType(writer, inst_ty.childType()); |
| | 1933 | try writer.print(")));\n", .{}); |
| | 1934 | |
| | 1935 | return local; |
| | 1936 | } |
| | 1937 | |
| 1813 | fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { | 1938 | fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { |
| 1814 | if (f.liveness.isUnused(inst)) return CValue.none; | 1939 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 1815 | | 1940 | |
| ... | @@ -2306,15 +2431,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc | ... | @@ -2306,15 +2431,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc |
| 2306 | const writer = f.object.writer(); | 2431 | const writer = f.object.writer(); |
| 2307 | const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data; | 2432 | const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data; |
| 2308 | const field_name = struct_obj.fields.keys()[index]; | 2433 | const field_name = struct_obj.fields.keys()[index]; |
| | 2434 | const field_val = struct_obj.fields.values()[index]; |
| | 2435 | const addrof = if (field_val.ty.zigTypeTag() == .Array) "" else "&"; |
| 2309 | | 2436 | |
| 2310 | const inst_ty = f.air.typeOfIndex(inst); | 2437 | const inst_ty = f.air.typeOfIndex(inst); |
| 2311 | const local = try f.allocLocal(inst_ty, .Const); | 2438 | const local = try f.allocLocal(inst_ty, .Const); |
| 2312 | switch (struct_ptr) { | 2439 | switch (struct_ptr) { |
| 2313 | .local_ref => |i| { | 2440 | .local_ref => |i| { |
| 2314 | try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) }); | 2441 | try writer.print(" = {s}t{d}.{};\n", .{ addrof, i, fmtIdent(field_name) }); |
| 2315 | }, | 2442 | }, |
| 2316 | else => { | 2443 | else => { |
| 2317 | try writer.writeAll(" = &"); | 2444 | try writer.print(" = {s}", .{addrof}); |
| 2318 | try f.writeCValue(writer, struct_ptr); | 2445 | try f.writeCValue(writer, struct_ptr); |
| 2319 | try writer.print("->{};\n", .{fmtIdent(field_name)}); | 2446 | try writer.print("->{};\n", .{fmtIdent(field_name)}); |
| 2320 | }, | 2447 | }, |
| ... | @@ -2529,7 +2656,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -2529,7 +2656,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2529 | const writer = f.object.writer(); | 2656 | const writer = f.object.writer(); |
| 2530 | const operand = try f.resolveInst(un_op); | 2657 | const operand = try f.resolveInst(un_op); |
| 2531 | | 2658 | |
| 2532 | try writer.writeAll(" = "); | 2659 | try writer.writeAll(" = ("); |
| | 2660 | try f.renderType(writer, inst_ty); |
| | 2661 | try writer.writeAll(")"); |
| 2533 | try f.writeCValue(writer, operand); | 2662 | try f.writeCValue(writer, operand); |
| 2534 | try writer.writeAll(";\n"); | 2663 | try writer.writeAll(";\n"); |
| 2535 | return local; | 2664 | return local; |