authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-17 12:15:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
log761f36ff93b5c551101d7f731a136c2d66093e76
tree32cdeecd69458186a5550c870ef0ea9637066f10
parenta804de13c8e2d7a6a99c55355f964f658a5a76bc

stage2: rework C backend for new AIR memory layout


2 files changed, 397 insertions(+), 265 deletions(-)

src/Air.zig+2
...@@ -200,6 +200,8 @@ pub const Inst = struct {...@@ -200,6 +200,8 @@ pub const Inst = struct {
200 ret,200 ret,
201 /// Returns a pointer to a global variable.201 /// Returns a pointer to a global variable.
202 /// Uses the `ty_pl` field. Index is into the `variables` array.202 /// Uses the `ty_pl` field. Index is into the `variables` array.
203 /// TODO this can be modeled simply as a constant with a decl ref and then
204 /// the variables array can be removed from Air.
203 varptr,205 varptr,
204 /// Write a value to a pointer. LHS is pointer, RHS is value.206 /// Write a value to a pointer. LHS is pointer, RHS is value.
205 /// Result type is always void.207 /// Result type is always void.
src/codegen/c.zig+395-265
...@@ -14,6 +14,7 @@ const Decl = Module.Decl;...@@ -14,6 +14,7 @@ const Decl = Module.Decl;
14const trace = @import("../tracy.zig").trace;14const trace = @import("../tracy.zig").trace;
15const LazySrcLoc = Module.LazySrcLoc;15const LazySrcLoc = Module.LazySrcLoc;
16const Air = @import("../Air.zig");16const Air = @import("../Air.zig");
17const Zir = @import("../Zir.zig");
17const Liveness = @import("../Liveness.zig");18const Liveness = @import("../Liveness.zig");
1819
19const Mutability = enum { Const, Mut };20const Mutability = enum { Const, Mut };
...@@ -25,7 +26,7 @@ pub const CValue = union(enum) {...@@ -25,7 +26,7 @@ pub const CValue = union(enum) {
25 /// Index into local_names, but take the address.26 /// Index into local_names, but take the address.
26 local_ref: usize,27 local_ref: usize,
27 /// A constant instruction, to be rendered inline.28 /// A constant instruction, to be rendered inline.
28 constant: Air.Inst.Index,29 constant: Air.Inst.Ref,
29 /// Index into the parameters30 /// Index into the parameters
30 arg: usize,31 arg: usize,
31 /// By-value32 /// By-value
...@@ -105,11 +106,12 @@ pub const Object = struct {...@@ -105,11 +106,12 @@ pub const Object = struct {
105 next_block_index: usize = 0,106 next_block_index: usize = 0,
106 indent_writer: IndentWriter(std.ArrayList(u8).Writer),107 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
107108
108 fn resolveInst(o: *Object, inst: Air.Inst.Index) !CValue {109 fn resolveInst(o: *Object, inst: Air.Inst.Ref) !CValue {
109 if (inst.value()) |_| {110 if (o.air.value(inst)) |_| {
110 return CValue{ .constant = inst };111 return CValue{ .constant = inst };
111 }112 }
112 return o.value_map.get(inst).?; // Instruction does not dominate all uses!113 const index = Air.refToIndex(inst).?;
114 return o.value_map.get(index).?; // Assertion means instruction does not dominate usage.
113 }115 }
114116
115 fn allocLocalValue(o: *Object) CValue {117 fn allocLocalValue(o: *Object) CValue {
...@@ -134,9 +136,8 @@ pub const Object = struct {...@@ -134,9 +136,8 @@ pub const Object = struct {
134 .local => |i| return w.print("t{d}", .{i}),136 .local => |i| return w.print("t{d}", .{i}),
135 .local_ref => |i| return w.print("&t{d}", .{i}),137 .local_ref => |i| return w.print("&t{d}", .{i}),
136 .constant => |inst| {138 .constant => |inst| {
137 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;139 const ty = o.air.typeOf(inst);
138 const ty = o.air.getRefType(ty_pl.ty);140 const val = o.air.value(inst).?;
139 const val = o.air.values[ty_pl.payload];
140 return o.dg.renderValue(w, ty, val);141 return o.dg.renderValue(w, ty, val);
141 },142 },
142 .arg => |i| return w.print("a{d}", .{i}),143 .arg => |i| return w.print("a{d}", .{i}),
...@@ -854,81 +855,87 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM...@@ -854,81 +855,87 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
854855
855 for (body) |inst| {856 for (body) |inst| {
856 const result_value = switch (air_tags[inst]) {857 const result_value = switch (air_tags[inst]) {
857 //// TODO use a different strategy for add that communicates to the optimizer858 // zig fmt: off
858 //// that wrapping is UB.859 .constant => unreachable, // excluded from function bodies
859 //.add => try genBinOp(o, inst.castTag(.add).?, " + "),860 .const_ty => unreachable, // excluded from function bodies
860 //.addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"),861 .arg => airArg(o),
861 //// TODO use a different strategy for sub that communicates to the optimizer862
862 //// that wrapping is UB.863 .breakpoint => try airBreakpoint(o),
863 //.sub => try genBinOp(o, inst.castTag(.sub).?, " - "),864 .unreach => try airUnreach(o),
864 //.subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"),865
865 //// TODO use a different strategy for mul that communicates to the optimizer866 // TODO use a different strategy for add that communicates to the optimizer
866 //// that wrapping is UB.867 // that wrapping is UB.
867 //.mul => try genBinOp(o, inst.castTag(.sub).?, " * "),868 .add => try airBinOp( o, inst, " + "),
868 //.mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"),869 .addwrap => try airWrapOp(o, inst, " + ", "addw_"),
869 //// TODO use a different strategy for div that communicates to the optimizer870 // TODO use a different strategy for sub that communicates to the optimizer
870 //// that wrapping is UB.871 // that wrapping is UB.
871 //.div => try genBinOp(o, inst.castTag(.div).?, " / "),872 .sub => try airBinOp( o, inst, " - "),
872873 .subwrap => try airWrapOp(o, inst, " - ", "subw_"),
873 //.constant => unreachable, // excluded from function bodies874 // TODO use a different strategy for mul that communicates to the optimizer
874 //.alloc => try genAlloc(o, inst.castTag(.alloc).?),875 // that wrapping is UB.
875 //.arg => genArg(o),876 .mul => try airBinOp( o, inst, " * "),
876 //.assembly => try genAsm(o, inst.castTag(.assembly).?),877 .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
877 //.block => try genBlock(o, inst.castTag(.block).?),878 // TODO use a different strategy for div that communicates to the optimizer
878 //.bitcast => try genBitcast(o, inst.castTag(.bitcast).?),879 // that wrapping is UB.
879 //.breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?),880 .div => try airBinOp( o, inst, " / "),
880 //.call => try genCall(o, inst.castTag(.call).?),881
881 //.cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "),882 .cmp_eq => try airBinOp(o, inst, " == "),
882 //.cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "),883 .cmp_gt => try airBinOp(o, inst, " > "),
883 //.cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "),884 .cmp_gte => try airBinOp(o, inst, " >= "),
884 //.cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "),885 .cmp_lt => try airBinOp(o, inst, " < "),
885 //.cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "),886 .cmp_lte => try airBinOp(o, inst, " <= "),
886 //.cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "),887 .cmp_neq => try airBinOp(o, inst, " != "),
887 //.dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?),888
888 //.intcast => try genIntCast(o, inst.castTag(.intcast).?),889 // bool_and and bool_or are non-short-circuit operations
889 //.load => try genLoad(o, inst.castTag(.load).?),890 .bool_and => try airBinOp(o, inst, " & "),
890 //.ret => try genRet(o, inst.castTag(.ret).?),891 .bool_or => try airBinOp(o, inst, " | "),
891 //.retvoid => try genRetVoid(o),892 .bit_and => try airBinOp(o, inst, " & "),
892 //.store => try genStore(o, inst.castTag(.store).?),893 .bit_or => try airBinOp(o, inst, " | "),
893 //.unreach => try genUnreach(o, inst.castTag(.unreach).?),894 .xor => try airBinOp(o, inst, " ^ "),
894 //.loop => try genLoop(o, inst.castTag(.loop).?),895 .not => try airUnOp( o, inst, "!"),
895 //.condbr => try genCondBr(o, inst.castTag(.condbr).?),896
896 //.br => try genBr(o, inst.castTag(.br).?),897 .optional_payload => try airOptionalPayload(o, inst),
897 //.br_void => try genBrVoid(o, inst.castTag(.br_void).?.block),898 .optional_payload_ptr => try airOptionalPayload(o, inst),
898 //.switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?),899
899 //// bool_and and bool_or are non-short-circuit operations900 .is_err => try airIsErr(o, inst, "", ".", "!="),
900 //.bool_and => try genBinOp(o, inst.castTag(.bool_and).?, " & "),901 .is_non_err => try airIsErr(o, inst, "", ".", "=="),
901 //.bool_or => try genBinOp(o, inst.castTag(.bool_or).?, " | "),902 .is_err_ptr => try airIsErr(o, inst, "*", "->", "!="),
902 //.bit_and => try genBinOp(o, inst.castTag(.bit_and).?, " & "),903 .is_non_err_ptr => try airIsErr(o, inst, "*", "->", "=="),
903 //.bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "),904
904 //.xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "),905 .is_null => try airIsNull(o, inst, "==", ""),
905 //.not => try genUnOp(o, inst.castTag(.not).?, "!"),906 .is_non_null => try airIsNull(o, inst, "!=", ""),
906 //.is_null => try genIsNull(o, inst.castTag(.is_null).?),907 .is_null_ptr => try airIsNull(o, inst, "==", "[0]"),
907 //.is_non_null => try genIsNull(o, inst.castTag(.is_non_null).?),908 .is_non_null_ptr => try airIsNull(o, inst, "!=", "[0]"),
908 //.is_null_ptr => try genIsNull(o, inst.castTag(.is_null_ptr).?),909
909 //.is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?),910 .alloc => try airAlloc(o, inst),
910 //.wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?),911 .assembly => try airAsm(o, inst),
911 //.optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?),912 .block => try airBlock(o, inst),
912 //.optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),913 .bitcast => try airBitcast(o, inst),
913 //.ref => try genRef(o, inst.castTag(.ref).?),914 .call => try airCall(o, inst),
914 //.struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),915 .dbg_stmt => try airDbgStmt(o, inst),
915916 .intcast => try airIntCast(o, inst),
916 //.is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="),917 .load => try airLoad(o, inst),
917 //.is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="),918 .ret => try airRet(o, inst),
918 //.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="),919 .store => try airStore(o, inst),
919 //.is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="),920 .loop => try airLoop(o, inst),
920921 .cond_br => try airCondBr(o, inst),
921 //.unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),922 .br => try airBr(o, inst),
922 //.unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),923 .switch_br => try airSwitchBr(o, inst),
923 //.unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),924 .wrap_optional => try airWrapOptional(o, inst),
924 //.unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),925 .ref => try airRef(o, inst),
925 //.wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),926 .struct_field_ptr => try airStructFieldPtr(o, inst),
926 //.wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),927 .varptr => try airVarPtr(o, inst),
927 //.br_block_flat => return o.dg.fail("TODO: C backend: implement codegen for br_block_flat", .{}),928
928 //.ptrtoint => return o.dg.fail("TODO: C backend: implement codegen for ptrtoint", .{}),929 .unwrap_errunion_payload => try airUnwrapErrUnionPay(o, inst),
929 //.varptr => try genVarPtr(o, inst.castTag(.varptr).?),930 .unwrap_errunion_err => try airUnwrapErrUnionErr(o, inst),
930 //.floatcast => return o.dg.fail("TODO: C backend: implement codegen for floatcast", .{}),931 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(o, inst),
931 else => return o.dg.fail("TODO: C backend: rework AIR memory layout", .{}),932 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(o, inst),
933 .wrap_errunion_payload => try airWrapErrUnionPay(o, inst),
934 .wrap_errunion_err => try airWrapErrUnionErr(o, inst),
935
936 .ptrtoint => return o.dg.fail("TODO: C backend: implement codegen for ptrtoint", .{}),
937 .floatcast => return o.dg.fail("TODO: C backend: implement codegen for floatcast", .{}),
938 // zig fmt: on
932 };939 };
933 switch (result_value) {940 switch (result_value) {
934 .none => {},941 .none => {},
...@@ -940,38 +947,37 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM...@@ -940,38 +947,37 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
940 try writer.writeAll("}");947 try writer.writeAll("}");
941}948}
942949
943fn genVarPtr(o: *Object, inst: *Inst.VarPtr) !CValue {950fn airVarPtr(o: *Object, inst: Air.Inst.Index) !CValue {
944 _ = o;951 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
945 return CValue{ .decl_ref = inst.variable.owner_decl };952 const variable = o.air.variables[ty_pl.payload];
953 return CValue{ .decl_ref = variable.owner_decl };
946}954}
947955
948fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue {956fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue {
949 const writer = o.writer();957 const writer = o.writer();
958 const inst_ty = o.air.typeOfIndex(inst);
950959
951 // First line: the variable used as data storage.960 // First line: the variable used as data storage.
952 const elem_type = alloc.base.ty.elemType();961 const elem_type = inst_ty.elemType();
953 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;962 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
954 const local = try o.allocLocal(elem_type, mutability);963 const local = try o.allocLocal(elem_type, mutability);
955 try writer.writeAll(";\n");964 try writer.writeAll(";\n");
956965
957 return CValue{ .local_ref = local.local };966 return CValue{ .local_ref = local.local };
958}967}
959968
960fn genArg(o: *Object) CValue {969fn airArg(o: *Object) CValue {
961 const i = o.next_arg_index;970 const i = o.next_arg_index;
962 o.next_arg_index += 1;971 o.next_arg_index += 1;
963 return .{ .arg = i };972 return .{ .arg = i };
964}973}
965974
966fn genRetVoid(o: *Object) !CValue {975fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue {
967 try o.writer().print("return;\n", .{});976 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
968 return CValue.none;977 const inst_ty = o.air.typeOfIndex(inst);
969}978 const operand = try o.resolveInst(ty_op.operand);
970
971fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
972 const operand = try o.resolveInst(inst.operand);
973 const writer = o.writer();979 const writer = o.writer();
974 const local = try o.allocLocal(inst.base.ty, .Const);980 const local = try o.allocLocal(inst_ty, .Const);
975 switch (operand) {981 switch (operand) {
976 .local_ref => |i| {982 .local_ref => |i| {
977 const wrapped: CValue = .{ .local = i };983 const wrapped: CValue = .{ .local = i };
...@@ -994,8 +1000,9 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -994,8 +1000,9 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
994 return local;1000 return local;
995}1001}
9961002
997fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {1003fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
998 const operand = try o.resolveInst(inst.operand);1004 const un_op = o.air.instructions.items(.data)[inst].un_op;
1005 const operand = try o.resolveInst(un_op);
999 const writer = o.writer();1006 const writer = o.writer();
1000 try writer.writeAll("return ");1007 try writer.writeAll("return ");
1001 try o.writeCValue(writer, operand);1008 try o.writeCValue(writer, operand);
...@@ -1003,26 +1010,29 @@ fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1003,26 +1010,29 @@ fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {
1003 return CValue.none;1010 return CValue.none;
1004}1011}
10051012
1006fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue {1013fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
1007 if (inst.base.isUnused())1014 if (o.liveness.isUnused(inst))
1008 return CValue.none;1015 return CValue.none;
10091016
1010 const from = try o.resolveInst(inst.operand);1017 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1018 const from = try o.resolveInst(ty_op.operand);
10111019
1012 const writer = o.writer();1020 const writer = o.writer();
1013 const local = try o.allocLocal(inst.base.ty, .Const);1021 const inst_ty = o.air.typeOfIndex(inst);
1022 const local = try o.allocLocal(inst_ty, .Const);
1014 try writer.writeAll(" = (");1023 try writer.writeAll(" = (");
1015 try o.dg.renderType(writer, inst.base.ty);1024 try o.dg.renderType(writer, inst_ty);
1016 try writer.writeAll(")");1025 try writer.writeAll(")");
1017 try o.writeCValue(writer, from);1026 try o.writeCValue(writer, from);
1018 try writer.writeAll(";\n");1027 try writer.writeAll(";\n");
1019 return local;1028 return local;
1020}1029}
10211030
1022fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {1031fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
1023 // *a = b;1032 // *a = b;
1024 const dest_ptr = try o.resolveInst(inst.lhs);1033 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1025 const src_val = try o.resolveInst(inst.rhs);1034 const dest_ptr = try o.resolveInst(bin_op.lhs);
1035 const src_val = try o.resolveInst(bin_op.rhs);
10261036
1027 const writer = o.writer();1037 const writer = o.writer();
1028 switch (dest_ptr) {1038 switch (dest_ptr) {
...@@ -1051,11 +1061,18 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {...@@ -1051,11 +1061,18 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
1051 return CValue.none;1061 return CValue.none;
1052}1062}
10531063
1054fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue {1064fn airWrapOp(
1055 if (inst.base.isUnused())1065 o: *Object,
1066 inst: Air.Inst.Index,
1067 str_op: [*:0]const u8,
1068 fn_op: [*:0]const u8,
1069) !CValue {
1070 if (o.liveness.isUnused(inst))
1056 return CValue.none;1071 return CValue.none;
10571072
1058 const int_info = inst.base.ty.intInfo(o.dg.module.getTarget());1073 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1074 const inst_ty = o.air.typeOfIndex(inst);
1075 const int_info = inst_ty.intInfo(o.dg.module.getTarget());
1059 const bits = int_info.bits;1076 const bits = int_info.bits;
10601077
1061 // if it's an unsigned int with non-arbitrary bit size then we can just add1078 // if it's an unsigned int with non-arbitrary bit size then we can just add
...@@ -1064,19 +1081,19 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c...@@ -1064,19 +1081,19 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1064 8, 16, 32, 64, 128 => true,1081 8, 16, 32, 64, 128 => true,
1065 else => false,1082 else => false,
1066 };1083 };
1067 if (ok_bits or inst.base.ty.tag() != .int_unsigned) {1084 if (ok_bits or inst_ty.tag() != .int_unsigned) {
1068 return try genBinOp(o, inst, str_op);1085 return try airBinOp(o, inst, str_op);
1069 }1086 }
1070 }1087 }
10711088
1072 if (bits > 64) {1089 if (bits > 64) {
1073 return o.dg.fail("TODO: C backend: genWrapOp for large integers", .{});1090 return o.dg.fail("TODO: C backend: airWrapOp for large integers", .{});
1074 }1091 }
10751092
1076 var min_buf: [80]u8 = undefined;1093 var min_buf: [80]u8 = undefined;
1077 const min = switch (int_info.signedness) {1094 const min = switch (int_info.signedness) {
1078 .unsigned => "0",1095 .unsigned => "0",
1079 else => switch (inst.base.ty.tag()) {1096 else => switch (inst_ty.tag()) {
1080 .c_short => "SHRT_MIN",1097 .c_short => "SHRT_MIN",
1081 .c_int => "INT_MIN",1098 .c_int => "INT_MIN",
1082 .c_long => "LONG_MIN",1099 .c_long => "LONG_MIN",
...@@ -1093,7 +1110,7 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c...@@ -1093,7 +1110,7 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1093 };1110 };
10941111
1095 var max_buf: [80]u8 = undefined;1112 var max_buf: [80]u8 = undefined;
1096 const max = switch (inst.base.ty.tag()) {1113 const max = switch (inst_ty.tag()) {
1097 .c_short => "SHRT_MAX",1114 .c_short => "SHRT_MAX",
1098 .c_ushort => "USHRT_MAX",1115 .c_ushort => "USHRT_MAX",
1099 .c_int => "INT_MAX",1116 .c_int => "INT_MAX",
...@@ -1117,14 +1134,14 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c...@@ -1117,14 +1134,14 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1117 },1134 },
1118 };1135 };
11191136
1120 const lhs = try o.resolveInst(inst.lhs);1137 const lhs = try o.resolveInst(bin_op.lhs);
1121 const rhs = try o.resolveInst(inst.rhs);1138 const rhs = try o.resolveInst(bin_op.rhs);
1122 const w = o.writer();1139 const w = o.writer();
11231140
1124 const ret = try o.allocLocal(inst.base.ty, .Mut);1141 const ret = try o.allocLocal(inst_ty, .Mut);
1125 try w.print(" = zig_{s}", .{fn_op});1142 try w.print(" = zig_{s}", .{fn_op});
11261143
1127 switch (inst.base.ty.tag()) {1144 switch (inst_ty.tag()) {
1128 .isize => try w.writeAll("isize"),1145 .isize => try w.writeAll("isize"),
1129 .c_short => try w.writeAll("short"),1146 .c_short => try w.writeAll("short"),
1130 .c_int => try w.writeAll("int"),1147 .c_int => try w.writeAll("int"),
...@@ -1161,15 +1178,17 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c...@@ -1161,15 +1178,17 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1161 return ret;1178 return ret;
1162}1179}
11631180
1164fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {1181fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1165 if (inst.base.isUnused())1182 if (o.liveness.isUnused(inst))
1166 return CValue.none;1183 return CValue.none;
11671184
1168 const lhs = try o.resolveInst(inst.lhs);1185 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1169 const rhs = try o.resolveInst(inst.rhs);1186 const lhs = try o.resolveInst(bin_op.lhs);
1187 const rhs = try o.resolveInst(bin_op.rhs);
11701188
1171 const writer = o.writer();1189 const writer = o.writer();
1172 const local = try o.allocLocal(inst.base.ty, .Const);1190 const inst_ty = o.air.typeOfIndex(inst);
1191 const local = try o.allocLocal(inst_ty, .Const);
11731192
1174 try writer.writeAll(" = ");1193 try writer.writeAll(" = ");
1175 try o.writeCValue(writer, lhs);1194 try o.writeCValue(writer, lhs);
...@@ -1180,14 +1199,16 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {...@@ -1180,14 +1199,16 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {
1180 return local;1199 return local;
1181}1200}
11821201
1183fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue {1202fn airUnOp(o: *Object, inst: Air.Inst.Index, operator: []const u8) !CValue {
1184 if (inst.base.isUnused())1203 if (o.liveness.isUnused(inst))
1185 return CValue.none;1204 return CValue.none;
11861205
1187 const operand = try o.resolveInst(inst.operand);1206 const un_op = o.air.instructions.items(.data)[inst].un_op;
1207 const operand = try o.resolveInst(un_op);
11881208
1189 const writer = o.writer();1209 const writer = o.writer();
1190 const local = try o.allocLocal(inst.base.ty, .Const);1210 const inst_ty = o.air.typeOfIndex(inst);
1211 const local = try o.allocLocal(inst_ty, .Const);
11911212
1192 try writer.print(" = {s}", .{operator});1213 try writer.print(" = {s}", .{operator});
1193 try o.writeCValue(writer, operand);1214 try o.writeCValue(writer, operand);
...@@ -1196,18 +1217,22 @@ fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue {...@@ -1196,18 +1217,22 @@ fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue {
1196 return local;1217 return local;
1197}1218}
11981219
1199fn genCall(o: *Object, inst: *Inst.Call) !CValue {1220fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
1200 if (inst.func.castTag(.constant)) |func_inst| {1221 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1201 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|1222 const extra = o.air.extraData(Air.Call, pl_op.payload);
1223 const args = @bitCast([]const Air.Inst.Ref, o.air.extra[extra.end..][0..extra.data.args_len]);
1224
1225 if (o.air.value(pl_op.operand)) |func_val| {
1226 const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn|
1202 extern_fn.data1227 extern_fn.data
1203 else if (func_inst.val.castTag(.function)) |func_payload|1228 else if (func_val.castTag(.function)) |func_payload|
1204 func_payload.data.owner_decl1229 func_payload.data.owner_decl
1205 else1230 else
1206 unreachable;1231 unreachable;
12071232
1208 const fn_ty = fn_decl.ty;1233 const fn_ty = fn_decl.ty;
1209 const ret_ty = fn_ty.fnReturnType();1234 const ret_ty = fn_ty.fnReturnType();
1210 const unused_result = inst.base.isUnused();1235 const unused_result = o.liveness.isUnused(inst);
1211 var result_local: CValue = .none;1236 var result_local: CValue = .none;
12121237
1213 const writer = o.writer();1238 const writer = o.writer();
...@@ -1221,17 +1246,15 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {...@@ -1221,17 +1246,15 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {
1221 }1246 }
1222 const fn_name = mem.spanZ(fn_decl.name);1247 const fn_name = mem.spanZ(fn_decl.name);
1223 try writer.print("{s}(", .{fn_name});1248 try writer.print("{s}(", .{fn_name});
1224 if (inst.args.len != 0) {1249 for (args) |arg, i| {
1225 for (inst.args) |arg, i| {1250 if (i != 0) {
1226 if (i > 0) {1251 try writer.writeAll(", ");
1227 try writer.writeAll(", ");1252 }
1228 }1253 if (o.air.value(arg)) |val| {
1229 if (arg.value()) |val| {1254 try o.dg.renderValue(writer, o.air.typeOf(arg), val);
1230 try o.dg.renderValue(writer, arg.ty, val);1255 } else {
1231 } else {1256 const val = try o.resolveInst(arg);
1232 const val = try o.resolveInst(arg);1257 try o.writeCValue(writer, val);
1233 try o.writeCValue(writer, val);
1234 }
1235 }1258 }
1236 }1259 }
1237 try writer.writeAll(");\n");1260 try writer.writeAll(");\n");
...@@ -1241,21 +1264,26 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {...@@ -1241,21 +1264,26 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {
1241 }1264 }
1242}1265}
12431266
1244fn genDbgStmt(o: *Object, inst: *Inst.DbgStmt) !CValue {1267fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue {
1245 _ = o;1268 const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt;
1246 _ = inst;1269 const writer = o.writer();
1247 // TODO emit #line directive here with line number and filename1270 try writer.print("#line {d}\n", .{dbg_stmt.line});
1248 return CValue.none;1271 return CValue.none;
1249}1272}
12501273
1251fn genBlock(o: *Object, inst: *Inst.Block) !CValue {1274fn airBlock(o: *Object, inst: Air.Inst.Index) !CValue {
1275 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1276 const extra = o.air.extraData(Air.Block, ty_pl.payload);
1277 const body = o.air.extra[extra.end..][0..extra.data.body_len];
1278
1252 const block_id: usize = o.next_block_index;1279 const block_id: usize = o.next_block_index;
1253 o.next_block_index += 1;1280 o.next_block_index += 1;
1254 const writer = o.writer();1281 const writer = o.writer();
12551282
1256 const result = if (inst.base.ty.tag() != .void and !inst.base.isUnused()) blk: {1283 const inst_ty = o.air.typeOfIndex(inst);
1284 const result = if (inst_ty.tag() != .void and !o.liveness.isUnused(inst)) blk: {
1257 // allocate a location for the result1285 // allocate a location for the result
1258 const local = try o.allocLocal(inst.base.ty, .Mut);1286 const local = try o.allocLocal(inst_ty, .Mut);
1259 try writer.writeAll(";\n");1287 try writer.writeAll(";\n");
1260 break :blk local;1288 break :blk local;
1261 } else CValue{ .none = {} };1289 } else CValue{ .none = {} };
...@@ -1265,42 +1293,44 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue {...@@ -1265,42 +1293,44 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue {
1265 .result = result,1293 .result = result,
1266 });1294 });
12671295
1268 try genBody(o, inst.body);1296 try genBody(o, body);
1269 try o.indent_writer.insertNewline();1297 try o.indent_writer.insertNewline();
1270 // label must be followed by an expression, add an empty one.1298 // label must be followed by an expression, add an empty one.
1271 try writer.print("zig_block_{d}:;\n", .{block_id});1299 try writer.print("zig_block_{d}:;\n", .{block_id});
1272 return result;1300 return result;
1273}1301}
12741302
1275fn genBr(o: *Object, inst: *Inst.Br) !CValue {1303fn airBr(o: *Object, inst: Air.Inst.Index) !CValue {
1276 const result = o.blocks.get(inst.block).?.result;1304 const branch = o.air.instructions.items(.data)[inst].br;
1305 const block = o.blocks.get(branch.block_inst).?;
1306 const result = block.result;
1277 const writer = o.writer();1307 const writer = o.writer();
12781308
1279 // If result is .none then the value of the block is unused.1309 // If result is .none then the value of the block is unused.
1280 if (inst.operand.ty.tag() != .void and result != .none) {1310 if (result != .none) {
1281 const operand = try o.resolveInst(inst.operand);1311 const operand = try o.resolveInst(branch.operand);
1282 try o.writeCValue(writer, result);1312 try o.writeCValue(writer, result);
1283 try writer.writeAll(" = ");1313 try writer.writeAll(" = ");
1284 try o.writeCValue(writer, operand);1314 try o.writeCValue(writer, operand);
1285 try writer.writeAll(";\n");1315 try writer.writeAll(";\n");
1286 }1316 }
12871317
1288 return genBrVoid(o, inst.block);1318 try o.writer().print("goto zig_block_{d};\n", .{block.block_id});
1289}
1290
1291fn genBrVoid(o: *Object, block: *Inst.Block) !CValue {
1292 try o.writer().print("goto zig_block_{d};\n", .{o.blocks.get(block).?.block_id});
1293 return CValue.none;1319 return CValue.none;
1294}1320}
12951321
1296fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {1322fn airBitcast(o: *Object, inst: Air.Inst.Index) !CValue {
1297 const operand = try o.resolveInst(inst.operand);1323 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1324 const operand = try o.resolveInst(ty_op.operand);
12981325
1299 const writer = o.writer();1326 const writer = o.writer();
1300 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {1327 const inst_ty = o.air.typeOfIndex(inst);
1301 const local = try o.allocLocal(inst.base.ty, .Const);1328 if (inst_ty.zigTypeTag() == .Pointer and
1329 o.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
1330 {
1331 const local = try o.allocLocal(inst_ty, .Const);
1302 try writer.writeAll(" = (");1332 try writer.writeAll(" = (");
1303 try o.dg.renderType(writer, inst.base.ty);1333 try o.dg.renderType(writer, inst_ty);
13041334
1305 try writer.writeAll(")");1335 try writer.writeAll(")");
1306 try o.writeCValue(writer, operand);1336 try o.writeCValue(writer, operand);
...@@ -1308,7 +1338,7 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1308,7 +1338,7 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
1308 return local;1338 return local;
1309 }1339 }
13101340
1311 const local = try o.allocLocal(inst.base.ty, .Mut);1341 const local = try o.allocLocal(inst_ty, .Mut);
1312 try writer.writeAll(";\n");1342 try writer.writeAll(";\n");
13131343
1314 try writer.writeAll("memcpy(&");1344 try writer.writeAll("memcpy(&");
...@@ -1322,79 +1352,124 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1322,79 +1352,124 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
1322 return local;1352 return local;
1323}1353}
13241354
1325fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue {1355fn airBreakpoint(o: *Object) !CValue {
1326 _ = inst;
1327 try o.writer().writeAll("zig_breakpoint();\n");1356 try o.writer().writeAll("zig_breakpoint();\n");
1328 return CValue.none;1357 return CValue.none;
1329}1358}
13301359
1331fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue {1360fn airUnreach(o: *Object) !CValue {
1332 _ = inst;
1333 try o.writer().writeAll("zig_unreachable();\n");1361 try o.writer().writeAll("zig_unreachable();\n");
1334 return CValue.none;1362 return CValue.none;
1335}1363}
13361364
1337fn genLoop(o: *Object, inst: *Inst.Loop) !CValue {1365fn airLoop(o: *Object, inst: Air.Inst.Index) !CValue {
1366 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1367 const loop = o.air.extraData(Air.Block, ty_pl.payload);
1368 const body = o.air.extra[loop.end..][0..loop.data.body_len];
1338 try o.writer().writeAll("while (true) ");1369 try o.writer().writeAll("while (true) ");
1339 try genBody(o, inst.body);1370 try genBody(o, body);
1340 try o.indent_writer.insertNewline();1371 try o.indent_writer.insertNewline();
1341 return CValue.none;1372 return CValue.none;
1342}1373}
13431374
1344fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue {1375fn airCondBr(o: *Object, inst: Air.Inst.Index) !CValue {
1345 const cond = try o.resolveInst(inst.condition);1376 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1377 const cond = try o.resolveInst(pl_op.operand);
1378 const extra = o.air.extraData(Air.CondBr, pl_op.payload);
1379 const then_body = o.air.extra[extra.end..][0..extra.data.then_body_len];
1380 const else_body = o.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1346 const writer = o.writer();1381 const writer = o.writer();
13471382
1348 try writer.writeAll("if (");1383 try writer.writeAll("if (");
1349 try o.writeCValue(writer, cond);1384 try o.writeCValue(writer, cond);
1350 try writer.writeAll(") ");1385 try writer.writeAll(") ");
1351 try genBody(o, inst.then_body);1386 try genBody(o, then_body);
1352 try writer.writeAll(" else ");1387 try writer.writeAll(" else ");
1353 try genBody(o, inst.else_body);1388 try genBody(o, else_body);
1354 try o.indent_writer.insertNewline();1389 try o.indent_writer.insertNewline();
13551390
1356 return CValue.none;1391 return CValue.none;
1357}1392}
13581393
1359fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue {1394fn airSwitchBr(o: *Object, inst: Air.Inst.Index) !CValue {
1360 const target = try o.resolveInst(inst.target);1395 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1396 const condition = try o.resolveInst(pl_op.operand);
1397 const condition_ty = o.air.typeOf(pl_op.operand);
1361 const writer = o.writer();1398 const writer = o.writer();
13621399
1363 try writer.writeAll("switch (");1400 try writer.writeAll("switch (");
1364 try o.writeCValue(writer, target);1401 try o.writeCValue(writer, condition);
1365 try writer.writeAll(") {\n");1402 try writer.writeAll(") {\n");
1366 o.indent_writer.pushIndent();1403 o.indent_writer.pushIndent();
13671404
1368 for (inst.cases) |case| {1405 // Need to rework Sema so that multiple cases are represented rather than
1369 try writer.writeAll("case ");1406 // getting branching logic inside the else, this way we get multiple case
1370 try o.dg.renderValue(writer, inst.target.ty, case.item);1407 // labels here rather than logic in the default case.
1371 try writer.writeAll(": ");1408 _ = condition_ty;
1372 // the case body must be noreturn so we don't need to insert a break1409 return o.dg.fail("TODO implement switch in C backend", .{});
1373 try genBody(o, case.body);1410
1374 try o.indent_writer.insertNewline();1411 //for (inst.cases) |case| {
1375 }1412 // try writer.writeAll("case ");
13761413 // try o.dg.renderValue(writer, condition_ty, case.item);
1377 try writer.writeAll("default: ");1414 // try writer.writeAll(": ");
1378 try genBody(o, inst.else_body);1415 // // the case body must be noreturn so we don't need to insert a break
1379 try o.indent_writer.insertNewline();1416 // try genBody(o, case.body);
13801417 // try o.indent_writer.insertNewline();
1381 o.indent_writer.popIndent();1418 //}
1382 try writer.writeAll("}\n");1419
1383 return CValue.none;1420 //try writer.writeAll("default: ");
1421 //try genBody(o, inst.else_body);
1422 //try o.indent_writer.insertNewline();
1423
1424 //o.indent_writer.popIndent();
1425 //try writer.writeAll("}\n");
1426 //return CValue.none;
1384}1427}
13851428
1386fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {1429fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
1387 if (as.base.isUnused() and !as.is_volatile)1430 const air_datas = o.air.instructions.items(.data);
1431 const air_extra = o.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1432 const zir = o.dg.decl.namespace.file_scope.zir;
1433 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
1434 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
1435 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
1436 const outputs_len = @truncate(u5, extended.small);
1437 const args_len = @truncate(u5, extended.small >> 5);
1438 const clobbers_len = @truncate(u5, extended.small >> 10);
1439 _ = clobbers_len; // TODO honor these
1440 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1441 const outputs = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end..][0..outputs_len]);
1442 const args = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end + outputs.len ..][0..args_len]);
1443
1444 if (outputs_len > 1) {
1445 return o.dg.fail("TODO implement codegen for asm with more than 1 output", .{});
1446 }
1447
1448 if (o.liveness.isUnused(inst) and !is_volatile)
1388 return CValue.none;1449 return CValue.none;
13891450
1451 var extra_i: usize = zir_extra.end;
1452 const output_constraint: ?[]const u8 = out: {
1453 var i: usize = 0;
1454 while (i < outputs_len) : (i += 1) {
1455 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
1456 extra_i = output.end;
1457 break :out zir.nullTerminatedString(output.data.constraint);
1458 }
1459 break :out null;
1460 };
1461 const args_extra_begin = extra_i;
1462
1390 const writer = o.writer();1463 const writer = o.writer();
1391 for (as.inputs) |i, index| {1464 for (args) |arg| {
1392 if (i[0] == '{' and i[i.len - 1] == '}') {1465 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1393 const reg = i[1 .. i.len - 1];1466 extra_i = input.end;
1394 const arg = as.args[index];1467 const constraint = zir.nullTerminatedString(input.data.constraint);
1468 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
1469 const reg = constraint[1 .. constraint.len - 1];
1395 const arg_c_value = try o.resolveInst(arg);1470 const arg_c_value = try o.resolveInst(arg);
1396 try writer.writeAll("register ");1471 try writer.writeAll("register ");
1397 try o.dg.renderType(writer, arg.ty);1472 try o.dg.renderType(writer, o.air.typeOf(arg));
13981473
1399 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });1474 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
1400 try o.writeCValue(writer, arg_c_value);1475 try o.writeCValue(writer, arg_c_value);
...@@ -1403,19 +1478,23 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {...@@ -1403,19 +1478,23 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
1403 return o.dg.fail("TODO non-explicit inline asm regs", .{});1478 return o.dg.fail("TODO non-explicit inline asm regs", .{});
1404 }1479 }
1405 }1480 }
1406 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";1481 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
1407 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });1482 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
1408 if (as.output_constraint) |_| {1483 if (output_constraint) |_| {
1409 return o.dg.fail("TODO: CBE inline asm output", .{});1484 return o.dg.fail("TODO: CBE inline asm output", .{});
1410 }1485 }
1411 if (as.inputs.len > 0) {1486 if (args.len > 0) {
1412 if (as.output_constraint == null) {1487 if (output_constraint == null) {
1413 try writer.writeAll(" :");1488 try writer.writeAll(" :");
1414 }1489 }
1415 try writer.writeAll(": ");1490 try writer.writeAll(": ");
1416 for (as.inputs) |i, index| {1491 extra_i = args_extra_begin;
1417 if (i[0] == '{' and i[i.len - 1] == '}') {1492 for (args) |_, index| {
1418 const reg = i[1 .. i.len - 1];1493 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1494 extra_i = input.end;
1495 const constraint = zir.nullTerminatedString(input.data.constraint);
1496 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
1497 const reg = constraint[1 .. constraint.len - 1];
1419 if (index > 0) {1498 if (index > 0) {
1420 try writer.writeAll(", ");1499 try writer.writeAll(", ");
1421 }1500 }
...@@ -1428,40 +1507,51 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {...@@ -1428,40 +1507,51 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
1428 }1507 }
1429 try writer.writeAll(");\n");1508 try writer.writeAll(");\n");
14301509
1431 if (as.base.isUnused())1510 if (o.liveness.isUnused(inst))
1432 return CValue.none;1511 return CValue.none;
14331512
1434 return o.dg.fail("TODO: C backend: inline asm expression result used", .{});1513 return o.dg.fail("TODO: C backend: inline asm expression result used", .{});
1435}1514}
14361515
1437fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue {1516fn airIsNull(
1517 o: *Object,
1518 inst: Air.Inst.Index,
1519 operator: [*:0]const u8,
1520 deref_suffix: [*:0]const u8,
1521) !CValue {
1522 if (o.liveness.isUnused(inst))
1523 return CValue.none;
1524
1525 const un_op = o.air.instructions.items(.data)[inst].un_op;
1438 const writer = o.writer();1526 const writer = o.writer();
1439 const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr;1527 const operand = try o.resolveInst(un_op);
1440 const operator = if (invert_logic) "!=" else "==";
1441 const maybe_deref = if (inst.base.tag == .is_null_ptr or inst.base.tag == .is_non_null_ptr) "[0]" else "";
1442 const operand = try o.resolveInst(inst.operand);
14431528
1444 const local = try o.allocLocal(Type.initTag(.bool), .Const);1529 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1445 try writer.writeAll(" = (");1530 try writer.writeAll(" = (");
1446 try o.writeCValue(writer, operand);1531 try o.writeCValue(writer, operand);
14471532
1448 if (inst.operand.ty.isPtrLikeOptional()) {1533 if (o.air.typeOf(un_op).isPtrLikeOptional()) {
1449 // operand is a regular pointer, test `operand !=/== NULL`1534 // operand is a regular pointer, test `operand !=/== NULL`
1450 try writer.print("){s} {s} NULL;\n", .{ maybe_deref, operator });1535 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
1451 } else {1536 } else {
1452 try writer.print("){s}.is_null {s} true;\n", .{ maybe_deref, operator });1537 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
1453 }1538 }
1454 return local;1539 return local;
1455}1540}
14561541
1457fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {1542fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
1543 if (o.liveness.isUnused(inst))
1544 return CValue.none;
1545
1546 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1458 const writer = o.writer();1547 const writer = o.writer();
1459 const operand = try o.resolveInst(inst.operand);1548 const operand = try o.resolveInst(ty_op.operand);
1549 const operand_ty = o.air.typeOf(ty_op.operand);
14601550
1461 const opt_ty = if (inst.operand.ty.zigTypeTag() == .Pointer)1551 const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
1462 inst.operand.ty.elemType()1552 operand_ty.elemType()
1463 else1553 else
1464 inst.operand.ty;1554 operand_ty;
14651555
1466 if (opt_ty.isPtrLikeOptional()) {1556 if (opt_ty.isPtrLikeOptional()) {
1467 // the operand is just a regular pointer, no need to do anything special.1557 // the operand is just a regular pointer, no need to do anything special.
...@@ -1469,10 +1559,11 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1469,10 +1559,11 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
1469 return operand;1559 return operand;
1470 }1560 }
14711561
1472 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";1562 const inst_ty = o.air.typeOfIndex(inst);
1473 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";1563 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
1564 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
14741565
1475 const local = try o.allocLocal(inst.base.ty, .Const);1566 const local = try o.allocLocal(inst_ty, .Const);
1476 try writer.print(" = {s}(", .{maybe_addrof});1567 try writer.print(" = {s}(", .{maybe_addrof});
1477 try o.writeCValue(writer, operand);1568 try o.writeCValue(writer, operand);
14781569
...@@ -1480,24 +1571,36 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1480,24 +1571,36 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
1480 return local;1571 return local;
1481}1572}
14821573
1483fn genRef(o: *Object, inst: *Inst.UnOp) !CValue {1574fn airRef(o: *Object, inst: Air.Inst.Index) !CValue {
1575 if (o.liveness.isUnused(inst))
1576 return CValue.none;
1577
1578 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1484 const writer = o.writer();1579 const writer = o.writer();
1485 const operand = try o.resolveInst(inst.operand);1580 const operand = try o.resolveInst(ty_op.operand);
14861581
1487 const local = try o.allocLocal(inst.base.ty, .Const);1582 const inst_ty = o.air.typeOfIndex(inst);
1583 const local = try o.allocLocal(inst_ty, .Const);
1488 try writer.writeAll(" = ");1584 try writer.writeAll(" = ");
1489 try o.writeCValue(writer, operand);1585 try o.writeCValue(writer, operand);
1490 try writer.writeAll(";\n");1586 try writer.writeAll(";\n");
1491 return local;1587 return local;
1492}1588}
14931589
1494fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {1590fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue {
1591 if (o.liveness.isUnused(inst))
1592 return CValue.none;
1593
1594 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1595 const extra = o.air.extraData(Air.StructField, ty_pl.payload).data;
1495 const writer = o.writer();1596 const writer = o.writer();
1496 const struct_ptr = try o.resolveInst(inst.struct_ptr);1597 const struct_ptr = try o.resolveInst(extra.struct_ptr);
1497 const struct_obj = inst.struct_ptr.ty.elemType().castTag(.@"struct").?.data;1598 const struct_ptr_ty = o.air.typeOf(extra.struct_ptr);
1498 const field_name = struct_obj.fields.keys()[inst.field_index];1599 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
1600 const field_name = struct_obj.fields.keys()[extra.field_index];
14991601
1500 const local = try o.allocLocal(inst.base.ty, .Const);1602 const inst_ty = o.air.typeOfIndex(inst);
1603 const local = try o.allocLocal(inst_ty, .Const);
1501 switch (struct_ptr) {1604 switch (struct_ptr) {
1502 .local_ref => |i| {1605 .local_ref => |i| {
1503 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });1606 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
...@@ -1512,17 +1615,20 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {...@@ -1512,17 +1615,20 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
1512}1615}
15131616
1514// *(E!T) -> E NOT *E1617// *(E!T) -> E NOT *E
1515fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {1618fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
1516 if (inst.base.isUnused())1619 if (o.liveness.isUnused(inst))
1517 return CValue.none;1620 return CValue.none;
15181621
1622 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1623 const inst_ty = o.air.typeOfIndex(inst);
1519 const writer = o.writer();1624 const writer = o.writer();
1520 const operand = try o.resolveInst(inst.operand);1625 const operand = try o.resolveInst(ty_op.operand);
1626 const operand_ty = o.air.typeOf(ty_op.operand);
15211627
1522 const payload_ty = inst.operand.ty.errorUnionChild();1628 const payload_ty = operand_ty.errorUnionChild();
1523 if (!payload_ty.hasCodeGenBits()) {1629 if (!payload_ty.hasCodeGenBits()) {
1524 if (inst.operand.ty.zigTypeTag() == .Pointer) {1630 if (operand_ty.zigTypeTag() == .Pointer) {
1525 const local = try o.allocLocal(inst.base.ty, .Const);1631 const local = try o.allocLocal(inst_ty, .Const);
1526 try writer.writeAll(" = *");1632 try writer.writeAll(" = *");
1527 try o.writeCValue(writer, operand);1633 try o.writeCValue(writer, operand);
1528 try writer.writeAll(";\n");1634 try writer.writeAll(";\n");
...@@ -1532,9 +1638,9 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1532,9 +1638,9 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1532 }1638 }
1533 }1639 }
15341640
1535 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";1641 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
15361642
1537 const local = try o.allocLocal(inst.base.ty, .Const);1643 const local = try o.allocLocal(inst_ty, .Const);
1538 try writer.writeAll(" = (");1644 try writer.writeAll(" = (");
1539 try o.writeCValue(writer, operand);1645 try o.writeCValue(writer, operand);
15401646
...@@ -1542,22 +1648,25 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1542,22 +1648,25 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1542 return local;1648 return local;
1543}1649}
15441650
1545fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {1651fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
1546 if (inst.base.isUnused())1652 if (o.liveness.isUnused(inst))
1547 return CValue.none;1653 return CValue.none;
15481654
1655 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1549 const writer = o.writer();1656 const writer = o.writer();
1550 const operand = try o.resolveInst(inst.operand);1657 const operand = try o.resolveInst(ty_op.operand);
1658 const operand_ty = o.air.typeOf(ty_op.operand);
15511659
1552 const payload_ty = inst.operand.ty.errorUnionChild();1660 const payload_ty = operand_ty.errorUnionChild();
1553 if (!payload_ty.hasCodeGenBits()) {1661 if (!payload_ty.hasCodeGenBits()) {
1554 return CValue.none;1662 return CValue.none;
1555 }1663 }
15561664
1557 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";1665 const inst_ty = o.air.typeOfIndex(inst);
1558 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";1666 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
1667 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
15591668
1560 const local = try o.allocLocal(inst.base.ty, .Const);1669 const local = try o.allocLocal(inst_ty, .Const);
1561 try writer.print(" = {s}(", .{maybe_addrof});1670 try writer.print(" = {s}(", .{maybe_addrof});
1562 try o.writeCValue(writer, operand);1671 try o.writeCValue(writer, operand);
15631672
...@@ -1565,54 +1674,75 @@ fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1565,54 +1674,75 @@ fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1565 return local;1674 return local;
1566}1675}
15671676
1568fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue {1677fn airWrapOptional(o: *Object, inst: Air.Inst.Index) !CValue {
1678 if (o.liveness.isUnused(inst))
1679 return CValue.none;
1680
1681 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1569 const writer = o.writer();1682 const writer = o.writer();
1570 const operand = try o.resolveInst(inst.operand);1683 const operand = try o.resolveInst(ty_op.operand);
15711684
1572 if (inst.base.ty.isPtrLikeOptional()) {1685 const inst_ty = o.air.typeOfIndex(inst);
1686 if (inst_ty.isPtrLikeOptional()) {
1573 // the operand is just a regular pointer, no need to do anything special.1687 // the operand is just a regular pointer, no need to do anything special.
1574 return operand;1688 return operand;
1575 }1689 }
15761690
1577 // .wrap_optional is used to convert non-optionals into optionals so it can never be null.1691 // .wrap_optional is used to convert non-optionals into optionals so it can never be null.
1578 const local = try o.allocLocal(inst.base.ty, .Const);1692 const local = try o.allocLocal(inst_ty, .Const);
1579 try writer.writeAll(" = { .is_null = false, .payload =");1693 try writer.writeAll(" = { .is_null = false, .payload =");
1580 try o.writeCValue(writer, operand);1694 try o.writeCValue(writer, operand);
1581 try writer.writeAll("};\n");1695 try writer.writeAll("};\n");
1582 return local;1696 return local;
1583}1697}
1584fn genWrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {1698fn airWrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
1699 if (o.liveness.isUnused(inst))
1700 return CValue.none;
1701
1585 const writer = o.writer();1702 const writer = o.writer();
1586 const operand = try o.resolveInst(inst.operand);1703 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1704 const operand = try o.resolveInst(ty_op.operand);
15871705
1588 const local = try o.allocLocal(inst.base.ty, .Const);1706 const inst_ty = o.air.typeOfIndex(inst);
1707 const local = try o.allocLocal(inst_ty, .Const);
1589 try writer.writeAll(" = { .error = ");1708 try writer.writeAll(" = { .error = ");
1590 try o.writeCValue(writer, operand);1709 try o.writeCValue(writer, operand);
1591 try writer.writeAll(" };\n");1710 try writer.writeAll(" };\n");
1592 return local;1711 return local;
1593}1712}
1594fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {1713
1714fn airWrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
1715 if (o.liveness.isUnused(inst))
1716 return CValue.none;
1717
1718 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1595 const writer = o.writer();1719 const writer = o.writer();
1596 const operand = try o.resolveInst(inst.operand);1720 const operand = try o.resolveInst(ty_op.operand);
15971721
1598 const local = try o.allocLocal(inst.base.ty, .Const);1722 const inst_ty = o.air.typeOfIndex(inst);
1723 const local = try o.allocLocal(inst_ty, .Const);
1599 try writer.writeAll(" = { .error = 0, .payload = ");1724 try writer.writeAll(" = { .error = 0, .payload = ");
1600 try o.writeCValue(writer, operand);1725 try o.writeCValue(writer, operand);
1601 try writer.writeAll(" };\n");1726 try writer.writeAll(" };\n");
1602 return local;1727 return local;
1603}1728}
16041729
1605fn genIsErr(1730fn airIsErr(
1606 o: *Object,1731 o: *Object,
1607 inst: *Inst.UnOp,1732 inst: Air.Inst.Index,
1608 deref_prefix: [*:0]const u8,1733 deref_prefix: [*:0]const u8,
1609 deref_suffix: [*:0]const u8,1734 deref_suffix: [*:0]const u8,
1610 op_str: [*:0]const u8,1735 op_str: [*:0]const u8,
1611) !CValue {1736) !CValue {
1737 if (o.liveness.isUnused(inst))
1738 return CValue.none;
1739
1740 const un_op = o.air.instructions.items(.data)[inst].un_op;
1612 const writer = o.writer();1741 const writer = o.writer();
1613 const operand = try o.resolveInst(inst.operand);1742 const operand = try o.resolveInst(un_op);
1743 const operand_ty = o.air.typeOf(un_op);
1614 const local = try o.allocLocal(Type.initTag(.bool), .Const);1744 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1615 const payload_ty = inst.operand.ty.errorUnionChild();1745 const payload_ty = operand_ty.errorUnionChild();
1616 if (!payload_ty.hasCodeGenBits()) {1746 if (!payload_ty.hasCodeGenBits()) {
1617 try writer.print(" = {s}", .{deref_prefix});1747 try writer.print(" = {s}", .{deref_prefix});
1618 try o.writeCValue(writer, operand);1748 try o.writeCValue(writer, operand);