| ... | ... | @@ -9,6 +9,7 @@ const math = std.math; |
| 9 | 9 | |
| 10 | 10 | const Module = @import("../Module.zig"); |
| 11 | 11 | const TypedValue = @import("../TypedValue.zig"); |
| 12 | const Zir = @import("../Zir.zig"); |
| 12 | 13 | const Air = @import("../Air.zig"); |
| 13 | 14 | const Liveness = @import("../Liveness.zig"); |
| 14 | 15 | |
| ... | ... | @@ -488,9 +489,9 @@ pub const DeclGen = struct { |
| 488 | 489 | llvm_param[i] = try self.getLLVMType(fn_param); |
| 489 | 490 | } |
| 490 | 491 | |
| 491 | | const fn_type = llvm.Type.functionType( |
| 492 | const fn_type = llvm.functionType( |
| 492 | 493 | try self.getLLVMType(return_type), |
| 493 | | if (fn_param_len == 0) null else llvm_param.ptr, |
| 494 | llvm_param.ptr, |
| 494 | 495 | @intCast(c_uint, fn_param_len), |
| 495 | 496 | .False, |
| 496 | 497 | ); |
| ... | ... | @@ -750,7 +751,7 @@ pub const FuncGen = struct { |
| 750 | 751 | fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void { |
| 751 | 752 | const air_tags = self.air.instructions.items(.tag); |
| 752 | 753 | for (body) |inst| { |
| 753 | | const opt_value = switch (air_tags[inst]) { |
| 754 | const opt_value: ?*const llvm.Value = switch (air_tags[inst]) { |
| 754 | 755 | .add => try self.airAdd(inst), |
| 755 | 756 | .sub => try self.airSub(inst), |
| 756 | 757 | |
| ... | ... | @@ -775,6 +776,7 @@ pub const FuncGen = struct { |
| 775 | 776 | .call => try self.airCall(inst), |
| 776 | 777 | .cond_br => try self.airCondBr(inst), |
| 777 | 778 | .intcast => try self.airIntCast(inst), |
| 779 | .ptrtoint => try self.airPtrToInt(inst), |
| 778 | 780 | .load => try self.airLoad(inst), |
| 779 | 781 | .loop => try self.airLoop(inst), |
| 780 | 782 | .not => try self.airNot(inst), |
| ... | ... | @@ -783,6 +785,7 @@ pub const FuncGen = struct { |
| 783 | 785 | .unreach => self.airUnreach(inst), |
| 784 | 786 | .optional_payload => try self.airOptionalPayload(inst, false), |
| 785 | 787 | .optional_payload_ptr => try self.airOptionalPayload(inst, true), |
| 788 | .assembly => try self.airAssembly(inst), |
| 786 | 789 | .dbg_stmt => blk: { |
| 787 | 790 | // TODO: implement debug info |
| 788 | 791 | break :blk null; |
| ... | ... | @@ -821,7 +824,7 @@ pub const FuncGen = struct { |
| 821 | 824 | // Do we need that? |
| 822 | 825 | const call = self.builder.buildCall( |
| 823 | 826 | llvm_fn, |
| 824 | | if (args.len == 0) null else llvm_param_vals.ptr, |
| 827 | llvm_param_vals.ptr, |
| 825 | 828 | @intCast(c_uint, args.len), |
| 826 | 829 | "", |
| 827 | 830 | ); |
| ... | ... | @@ -986,6 +989,128 @@ pub const FuncGen = struct { |
| 986 | 989 | return null; |
| 987 | 990 | } |
| 988 | 991 | |
| 992 | fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 993 | // Eventually, the Zig compiler needs to be reworked to have inline assembly go |
| 994 | // through the same parsing code regardless of backend, and have LLVM-flavored |
| 995 | // inline assembly be *output* from that assembler. |
| 996 | // We don't have such an assembler implemented yet though. For now, this |
| 997 | // implementation feeds the inline assembly code directly to LLVM, same |
| 998 | // as stage1. |
| 999 | |
| 1000 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1001 | const air_asm = self.air.extraData(Air.Asm, ty_pl.payload); |
| 1002 | const zir = self.dg.decl.namespace.file_scope.zir; |
| 1003 | const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; |
| 1004 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); |
| 1005 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); |
| 1006 | const outputs_len = @truncate(u5, extended.small); |
| 1007 | const args_len = @truncate(u5, extended.small >> 5); |
| 1008 | const clobbers_len = @truncate(u5, extended.small >> 10); |
| 1009 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; |
| 1010 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..outputs_len]); |
| 1011 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end + outputs.len ..][0..args_len]); |
| 1012 | if (outputs_len > 1) { |
| 1013 | return self.todo("implement llvm codegen for asm with more than 1 output", .{}); |
| 1014 | } |
| 1015 | |
| 1016 | var extra_i: usize = zir_extra.end; |
| 1017 | const output_constraint: ?[]const u8 = out: { |
| 1018 | var i: usize = 0; |
| 1019 | while (i < outputs_len) : (i += 1) { |
| 1020 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); |
| 1021 | extra_i = output.end; |
| 1022 | break :out zir.nullTerminatedString(output.data.constraint); |
| 1023 | } |
| 1024 | break :out null; |
| 1025 | }; |
| 1026 | |
| 1027 | if (!is_volatile and self.liveness.isUnused(inst)) { |
| 1028 | return null; |
| 1029 | } |
| 1030 | |
| 1031 | var llvm_constraints: std.ArrayListUnmanaged(u8) = .{}; |
| 1032 | defer llvm_constraints.deinit(self.gpa); |
| 1033 | |
| 1034 | var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); |
| 1035 | defer arena_allocator.deinit(); |
| 1036 | const arena = &arena_allocator.allocator; |
| 1037 | |
| 1038 | const llvm_params_len = args.len + @boolToInt(output_constraint != null); |
| 1039 | const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); |
| 1040 | const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); |
| 1041 | |
| 1042 | var llvm_param_i: usize = 0; |
| 1043 | var total_i: usize = 0; |
| 1044 | |
| 1045 | if (output_constraint) |constraint| { |
| 1046 | try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); |
| 1047 | if (total_i != 0) { |
| 1048 | llvm_constraints.appendAssumeCapacity(','); |
| 1049 | } |
| 1050 | llvm_constraints.appendSliceAssumeCapacity(constraint); |
| 1051 | |
| 1052 | total_i += 1; |
| 1053 | } |
| 1054 | |
| 1055 | for (args) |arg| { |
| 1056 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); |
| 1057 | extra_i = input.end; |
| 1058 | const constraint = zir.nullTerminatedString(input.data.constraint); |
| 1059 | const arg_llvm_value = try self.resolveInst(arg); |
| 1060 | |
| 1061 | llvm_param_values[llvm_param_i] = arg_llvm_value; |
| 1062 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); |
| 1063 | |
| 1064 | try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); |
| 1065 | if (total_i != 0) { |
| 1066 | llvm_constraints.appendAssumeCapacity(','); |
| 1067 | } |
| 1068 | llvm_constraints.appendSliceAssumeCapacity(constraint); |
| 1069 | |
| 1070 | llvm_param_i += 1; |
| 1071 | total_i += 1; |
| 1072 | } |
| 1073 | |
| 1074 | const clobbers = zir.extra[extra_i..][0..clobbers_len]; |
| 1075 | for (clobbers) |clobber_index| { |
| 1076 | const clobber = zir.nullTerminatedString(clobber_index); |
| 1077 | try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); |
| 1078 | if (total_i != 0) { |
| 1079 | llvm_constraints.appendAssumeCapacity(','); |
| 1080 | } |
| 1081 | llvm_constraints.appendSliceAssumeCapacity("~{"); |
| 1082 | llvm_constraints.appendSliceAssumeCapacity(clobber); |
| 1083 | llvm_constraints.appendSliceAssumeCapacity("}"); |
| 1084 | |
| 1085 | total_i += 1; |
| 1086 | } |
| 1087 | |
| 1088 | const ret_ty = self.air.typeOfIndex(inst); |
| 1089 | const ret_llvm_ty = try self.dg.getLLVMType(ret_ty); |
| 1090 | const llvm_fn_ty = llvm.functionType( |
| 1091 | ret_llvm_ty, |
| 1092 | llvm_param_types.ptr, |
| 1093 | @intCast(c_uint, llvm_param_types.len), |
| 1094 | .False, |
| 1095 | ); |
| 1096 | const asm_fn = llvm.getInlineAsm( |
| 1097 | llvm_fn_ty, |
| 1098 | asm_source.ptr, |
| 1099 | asm_source.len, |
| 1100 | llvm_constraints.items.ptr, |
| 1101 | llvm_constraints.items.len, |
| 1102 | llvm.Bool.fromBool(is_volatile), |
| 1103 | .False, |
| 1104 | .ATT, |
| 1105 | ); |
| 1106 | return self.builder.buildCall( |
| 1107 | asm_fn, |
| 1108 | llvm_param_values.ptr, |
| 1109 | @intCast(c_uint, llvm_param_values.len), |
| 1110 | "", |
| 1111 | ); |
| 1112 | } |
| 1113 | |
| 989 | 1114 | fn airIsNonNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value { |
| 990 | 1115 | if (self.liveness.isUnused(inst)) |
| 991 | 1116 | return null; |
| ... | ... | @@ -1087,6 +1212,16 @@ pub const FuncGen = struct { |
| 1087 | 1212 | return self.builder.buildIntCast2(operand, try self.dg.getLLVMType(inst_ty), llvm.Bool.fromBool(signed), ""); |
| 1088 | 1213 | } |
| 1089 | 1214 | |
| 1215 | fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1216 | if (self.liveness.isUnused(inst)) |
| 1217 | return null; |
| 1218 | |
| 1219 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1220 | const operand = try self.resolveInst(un_op); |
| 1221 | const dest_llvm_ty = try self.dg.getLLVMType(self.air.typeOfIndex(inst)); |
| 1222 | return self.builder.buildPtrToInt(operand, dest_llvm_ty, ""); |
| 1223 | } |
| 1224 | |
| 1090 | 1225 | fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1091 | 1226 | if (self.liveness.isUnused(inst)) |
| 1092 | 1227 | return null; |
| ... | ... | @@ -1168,7 +1303,7 @@ pub const FuncGen = struct { |
| 1168 | 1303 | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1169 | 1304 | _ = inst; |
| 1170 | 1305 | const llvn_fn = self.getIntrinsic("llvm.debugtrap"); |
| 1171 | | _ = self.builder.buildCall(llvn_fn, null, 0, ""); |
| 1306 | _ = self.builder.buildCall(llvn_fn, undefined, 0, ""); |
| 1172 | 1307 | return null; |
| 1173 | 1308 | } |
| 1174 | 1309 | |