diff options
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/arm.zig | 206 | ||||
| -rw-r--r-- | src/codegen/c.zig | 55 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 120 | ||||
| -rw-r--r-- | src/codegen/llvm/bindings.zig | 17 | ||||
| -rw-r--r-- | src/codegen/wasm.zig | 26 |
5 files changed, 351 insertions, 73 deletions
diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 891a9e100b..3743afd50f 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -192,7 +192,7 @@ pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; /// Represents an instruction in the ARM instruction set architecture pub const Instruction = union(enum) { - DataProcessing: packed struct { + data_processing: packed struct { // Note to self: The order of the fields top-to-bottom is // right-to-left in the actual 32-bit int representation op2: u12, @@ -204,7 +204,7 @@ pub const Instruction = union(enum) { fixed: u2 = 0b00, cond: u4, }, - Multiply: packed struct { + multiply: packed struct { rn: u4, fixed_1: u4 = 0b1001, rm: u4, @@ -215,7 +215,7 @@ pub const Instruction = union(enum) { fixed_2: u6 = 0b000000, cond: u4, }, - MultiplyLong: packed struct { + multiply_long: packed struct { rn: u4, fixed_1: u4 = 0b1001, rm: u4, @@ -227,7 +227,17 @@ pub const Instruction = union(enum) { fixed_2: u5 = 0b00001, cond: u4, }, - SingleDataTransfer: packed struct { + integer_saturating_arithmetic: packed struct { + rm: u4, + fixed_1: u8 = 0b0000_0101, + rd: u4, + rn: u4, + fixed_2: u1 = 0b0, + opc: u2, + fixed_3: u5 = 0b00010, + cond: u4, + }, + single_data_transfer: packed struct { offset: u12, rd: u4, rn: u4, @@ -240,7 +250,7 @@ pub const Instruction = union(enum) { fixed: u2 = 0b01, cond: u4, }, - ExtraLoadStore: packed struct { + extra_load_store: packed struct { imm4l: u4, fixed_1: u1 = 0b1, op2: u2, @@ -256,7 +266,7 @@ pub const Instruction = union(enum) { fixed_3: u3 = 0b000, cond: u4, }, - BlockDataTransfer: packed struct { + block_data_transfer: packed struct { register_list: u16, rn: u4, load_store: u1, @@ -267,25 +277,25 @@ pub const Instruction = union(enum) { fixed: u3 = 0b100, cond: u4, }, - Branch: packed struct { + branch: packed struct { offset: u24, link: u1, fixed: u3 = 0b101, cond: u4, }, - BranchExchange: packed struct { + branch_exchange: packed struct { rn: u4, fixed_1: u1 = 0b1, link: u1, fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, cond: u4, }, - SupervisorCall: packed struct { + supervisor_call: packed struct { comment: u24, fixed: u4 = 0b1111, cond: u4, }, - Breakpoint: packed struct { + breakpoint: packed struct { imm4: u4, fixed_1: u4 = 0b0111, imm12: u12, @@ -293,7 +303,7 @@ pub const Instruction = union(enum) { }, /// Represents the possible operations which can be performed by a - /// DataProcessing instruction + /// Data Processing instruction const Opcode = enum(u4) { // Rd := Op1 AND Op2 @"and", @@ -530,16 +540,17 @@ pub const Instruction = union(enum) { pub fn toU32(self: Instruction) u32 { return switch (self) { - .DataProcessing => |v| @bitCast(u32, v), - .Multiply => |v| @bitCast(u32, v), - .MultiplyLong => |v| @bitCast(u32, v), - .SingleDataTransfer => |v| @bitCast(u32, v), - .ExtraLoadStore => |v| @bitCast(u32, v), - .BlockDataTransfer => |v| @bitCast(u32, v), - .Branch => |v| @bitCast(u32, v), - .BranchExchange => |v| @bitCast(u32, v), - .SupervisorCall => |v| @bitCast(u32, v), - .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), + .data_processing => |v| @bitCast(u32, v), + .multiply => |v| @bitCast(u32, v), + .multiply_long => |v| @bitCast(u32, v), + .integer_saturating_arithmetic => |v| @bitCast(u32, v), + .single_data_transfer => |v| @bitCast(u32, v), + .extra_load_store => |v| @bitCast(u32, v), + .block_data_transfer => |v| @bitCast(u32, v), + .branch => |v| @bitCast(u32, v), + .branch_exchange => |v| @bitCast(u32, v), + .supervisor_call => |v| @bitCast(u32, v), + .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), }; } @@ -554,7 +565,7 @@ pub const Instruction = union(enum) { op2: Operand, ) Instruction { return Instruction{ - .DataProcessing = .{ + .data_processing = .{ .cond = @enumToInt(cond), .i = @boolToInt(op2 == .Immediate), .opcode = @enumToInt(opcode), @@ -573,7 +584,7 @@ pub const Instruction = union(enum) { top: bool, ) Instruction { return Instruction{ - .DataProcessing = .{ + .data_processing = .{ .cond = @enumToInt(cond), .i = 1, .opcode = if (top) 0b1010 else 0b1000, @@ -594,7 +605,7 @@ pub const Instruction = union(enum) { ra: ?Register, ) Instruction { return Instruction{ - .Multiply = .{ + .multiply = .{ .cond = @enumToInt(cond), .accumulate = @boolToInt(ra != null), .set_cond = set_cond, @@ -617,7 +628,7 @@ pub const Instruction = union(enum) { rn: Register, ) Instruction { return Instruction{ - .MultiplyLong = .{ + .multiply_long = .{ .cond = @enumToInt(cond), .unsigned = signed, .accumulate = accumulate, @@ -630,6 +641,24 @@ pub const Instruction = union(enum) { }; } + fn integerSaturationArithmetic( + cond: Condition, + rd: Register, + rm: Register, + rn: Register, + opc: u2, + ) Instruction { + return Instruction{ + .integer_saturating_arithmetic = .{ + .rm = rm.id(), + .rd = rd.id(), + .rn = rn.id(), + .opc = opc, + .cond = @enumToInt(cond), + }, + }; + } + fn singleDataTransfer( cond: Condition, rd: Register, @@ -642,7 +671,7 @@ pub const Instruction = union(enum) { load_store: u1, ) Instruction { return Instruction{ - .SingleDataTransfer = .{ + .single_data_transfer = .{ .cond = @enumToInt(cond), .rn = rn.id(), .rd = rd.id(), @@ -678,7 +707,7 @@ pub const Instruction = union(enum) { }; return Instruction{ - .ExtraLoadStore = .{ + .extra_load_store = .{ .imm4l = imm4l, .op2 = op2, .imm4h = imm4h, @@ -705,7 +734,7 @@ pub const Instruction = union(enum) { load_store: u1, ) Instruction { return Instruction{ - .BlockDataTransfer = .{ + .block_data_transfer = .{ .register_list = @bitCast(u16, reg_list), .rn = rn.id(), .load_store = load_store, @@ -720,7 +749,7 @@ pub const Instruction = union(enum) { fn branch(cond: Condition, offset: i26, link: u1) Instruction { return Instruction{ - .Branch = .{ + .branch = .{ .cond = @enumToInt(cond), .link = link, .offset = @bitCast(u24, @intCast(i24, offset >> 2)), @@ -730,7 +759,7 @@ pub const Instruction = union(enum) { fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { return Instruction{ - .BranchExchange = .{ + .branch_exchange = .{ .cond = @enumToInt(cond), .link = link, .rn = rn.id(), @@ -740,7 +769,7 @@ pub const Instruction = union(enum) { fn supervisorCall(cond: Condition, comment: u24) Instruction { return Instruction{ - .SupervisorCall = .{ + .supervisor_call = .{ .cond = @enumToInt(cond), .comment = comment, }, @@ -749,7 +778,7 @@ pub const Instruction = union(enum) { fn breakpoint(imm: u16) Instruction { return Instruction{ - .Breakpoint = .{ + .breakpoint = .{ .imm12 = @truncate(u12, imm >> 4), .imm4 = @truncate(u4, imm), }, @@ -873,6 +902,24 @@ pub const Instruction = union(enum) { return dataProcessing(cond, .mvn, 1, rd, .r0, op2); } + // Integer Saturating Arithmetic + + pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { + return integerSaturationArithmetic(cond, rd, rm, rn, 0b00); + } + + pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { + return integerSaturationArithmetic(cond, rd, rm, rn, 0b01); + } + + pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { + return integerSaturationArithmetic(cond, rd, rm, rn, 0b10); + } + + pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { + return integerSaturationArithmetic(cond, rd, rm, rn, 0b11); + } + // movw and movt pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { @@ -887,7 +934,7 @@ pub const Instruction = union(enum) { pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { return Instruction{ - .DataProcessing = .{ + .data_processing = .{ .cond = @enumToInt(cond), .i = 0, .opcode = if (psr == .spsr) 0b1010 else 0b1000, @@ -901,7 +948,7 @@ pub const Instruction = union(enum) { pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { return Instruction{ - .DataProcessing = .{ + .data_processing = .{ .cond = @enumToInt(cond), .i = 0, .opcode = if (psr == .spsr) 0b1011 else 0b1001, @@ -1142,6 +1189,79 @@ pub const Instruction = union(enum) { return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); } } + + pub const ShiftAmount = union(enum) { + immediate: u5, + register: Register, + + pub fn imm(immediate: u5) ShiftAmount { + return .{ + .immediate = immediate, + }; + } + + pub fn reg(register: Register) ShiftAmount { + return .{ + .register = register, + }; + } + }; + + pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), + .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), + }; + } + + pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), + .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), + }; + } + + pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), + .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), + }; + } + + pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), + .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), + }; + } + + pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), + .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), + }; + } + + pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), + .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), + }; + } + + pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), + .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), + }; + } + + pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { + return switch (shift) { + .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), + .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), + }; + } }; test "serialize instructions" { @@ -1221,6 +1341,10 @@ test "serialize instructions" { .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, }, + .{ // qadd r0, r7, r8 + .inst = Instruction.qadd(.al, .r0, .r7, .r8), + .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, + }, }; for (testcases) |case| { @@ -1262,6 +1386,20 @@ test "aliases" { .actual = Instruction.push(.al, .{ .r0, .r2 }), .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), }, + .{ // lsl r4, r5, #5 + .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)), + .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg( + .r5, + Instruction.Operand.Shift.imm(5, .logical_left), + )), + }, + .{ // asrs r1, r1, r3 + .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)), + .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg( + .r1, + Instruction.Operand.Shift.reg(.r3, .arithmetic_right), + )), + }, }; for (testcases) |case| { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a67e2438c2..2084b1e1ce 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -319,18 +319,20 @@ pub const DeclGen = struct { .Bool => return writer.print("{}", .{val.toBool()}), .Optional => { var opt_buf: Type.Payload.ElemType = undefined; - const child_type = t.optionalChild(&opt_buf); + const payload_type = t.optionalChild(&opt_buf); if (t.isPtrLikeOptional()) { - return dg.renderValue(writer, child_type, val); + return dg.renderValue(writer, payload_type, val); } try writer.writeByte('('); try dg.renderType(writer, t); - if (val.tag() == .null_value) { - try writer.writeAll("){ .is_null = true }"); - } else { - try writer.writeAll("){ .is_null = false, .payload = "); - try dg.renderValue(writer, child_type, val); + try writer.writeAll("){"); + if (val.castTag(.opt_payload)) |pl| { + const payload_val = pl.data; + try writer.writeAll(" .is_null = false, .payload = "); + try dg.renderValue(writer, payload_type, payload_val); try writer.writeAll(" }"); + } else { + try writer.writeAll(" .is_null = true }"); } }, .ErrorSet => { @@ -871,6 +873,9 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM .bit_or => try airBinOp(o, inst, " | "), .xor => try airBinOp(o, inst, " ^ "), + .shr => try airBinOp(o, inst, " >> "), + .shl => try airBinOp(o, inst, " << "), + .not => try airNot( o, inst), .optional_payload => try airOptionalPayload(o, inst), @@ -904,12 +909,19 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM .switch_br => try airSwitchBr(o, inst), .wrap_optional => try airWrapOptional(o, inst), .struct_field_ptr => try airStructFieldPtr(o, inst), + + .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0), + .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1), + .struct_field_ptr_index_2 => try airStructFieldPtrIndex(o, inst, 2), + .struct_field_ptr_index_3 => try airStructFieldPtrIndex(o, inst, 3), + .struct_field_val => try airStructFieldVal(o, inst), .slice_ptr => try airSliceField(o, inst, ".ptr;\n"), .slice_len => try airSliceField(o, inst, ".len;\n"), .ptr_elem_val => try airPtrElemVal(o, inst, "["), .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["), + .ptr_elem_ptr => try airPtrElemPtr(o, inst), .slice_elem_val => try airSliceElemVal(o, inst, "["), .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["), @@ -957,6 +969,13 @@ fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { return o.dg.fail("TODO: C backend: airPtrElemVal", .{}); } +fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + return o.dg.fail("TODO: C backend: airPtrElemPtr", .{}); +} + fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { const is_volatile = false; // TODO if (!is_volatile and o.liveness.isUnused(inst)) @@ -1638,15 +1657,31 @@ fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue { fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue { if (o.liveness.isUnused(inst)) - return CValue.none; + // TODO this @as is needed because of a stage1 bug + return @as(CValue, CValue.none); const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; const extra = o.air.extraData(Air.StructField, ty_pl.payload).data; - const writer = o.writer(); const struct_ptr = try o.resolveInst(extra.struct_operand); const struct_ptr_ty = o.air.typeOf(extra.struct_operand); + return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, extra.field_index); +} + +fn airStructFieldPtrIndex(o: *Object, inst: Air.Inst.Index, index: u8) !CValue { + if (o.liveness.isUnused(inst)) + // TODO this @as is needed because of a stage1 bug + return @as(CValue, CValue.none); + + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const struct_ptr = try o.resolveInst(ty_op.operand); + const struct_ptr_ty = o.air.typeOf(ty_op.operand); + return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, index); +} + +fn structFieldPtr(o: *Object, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { + const writer = o.writer(); const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data; - const field_name = struct_obj.fields.keys()[extra.field_index]; + const field_name = struct_obj.fields.keys()[index]; const inst_ty = o.air.typeOfIndex(inst); const local = try o.allocLocal(inst_ty, .Const); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2b678b0a69..0a715edbfd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -434,6 +434,8 @@ pub const Object = struct { }, else => |e| return e, }; + const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; + try self.updateDeclExports(module, decl, decl_exports); } pub fn updateDeclExports( @@ -442,7 +444,9 @@ pub const Object = struct { decl: *const Module.Decl, exports: []const *Module.Export, ) !void { - const llvm_fn = self.llvm_module.getNamedFunction(decl.name).?; + // If the module does not already have the function, we ignore this function call + // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`. + const llvm_fn = self.llvm_module.getNamedFunction(decl.name) orelse return; const is_extern = decl.val.tag() == .extern_fn; if (is_extern or exports.len != 0) { llvm_fn.setLinkage(.External); @@ -808,27 +812,22 @@ pub const DeclGen = struct { return self.todo("handle more array values", .{}); }, .Optional => { - if (!tv.ty.isPtrLikeOptional()) { - var buf: Type.Payload.ElemType = undefined; - const child_type = tv.ty.optionalChild(&buf); - const llvm_child_type = try self.llvmType(child_type); - - if (tv.val.tag() == .null_value) { - var optional_values: [2]*const llvm.Value = .{ - llvm_child_type.constNull(), - self.context.intType(1).constNull(), - }; - return self.context.constStruct(&optional_values, optional_values.len, .False); - } else { - var optional_values: [2]*const llvm.Value = .{ - try self.genTypedValue(.{ .ty = child_type, .val = tv.val }), - self.context.intType(1).constAllOnes(), - }; - return self.context.constStruct(&optional_values, optional_values.len, .False); - } - } else { + if (tv.ty.isPtrLikeOptional()) { return self.todo("implement const of optional pointer", .{}); } + var buf: Type.Payload.ElemType = undefined; + const payload_type = tv.ty.optionalChild(&buf); + const is_pl = !tv.val.isNull(); + const llvm_i1 = self.context.intType(1); + + const fields: [2]*const llvm.Value = .{ + try self.genTypedValue(.{ + .ty = payload_type, + .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), + }), + if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull(), + }; + return self.context.constStruct(&fields, fields.len, .False); }, .Fn => { const fn_decl = switch (tv.val.tag()) { @@ -995,6 +994,9 @@ pub const FuncGen = struct { .bit_or, .bool_or => try self.airOr(inst), .xor => try self.airXor(inst), + .shl => try self.airShl(inst), + .shr => try self.airShr(inst), + .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), .cmp_gte => try self.airCmp(inst, .gte), @@ -1037,9 +1039,15 @@ pub const FuncGen = struct { .struct_field_ptr => try self.airStructFieldPtr(inst), .struct_field_val => try self.airStructFieldVal(inst), + .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), + .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), + .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), + .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), + .slice_elem_val => try self.airSliceElemVal(inst), .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), .ptr_elem_val => try self.airPtrElemVal(inst), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), .optional_payload => try self.airOptionalPayload(inst, false), @@ -1295,11 +1303,35 @@ pub const FuncGen = struct { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const base_ptr = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const indices: [1]*const llvm.Value = .{rhs}; - const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + const ptr = if (self.air.typeOf(bin_op.lhs).isSinglePointer()) ptr: { + // If this is a single-item pointer to an array, we need another index in the GEP. + const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs }; + break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + } else ptr: { + const indices: [1]*const llvm.Value = .{rhs}; + break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + }; return self.builder.buildLoad(ptr, ""); } + fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const base_ptr = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + if (self.air.typeOf(bin_op.lhs).isSinglePointer()) { + // If this is a single-item pointer to an array, we need another index in the GEP. + const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs }; + return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + } else { + const indices: [1]*const llvm.Value = .{rhs}; + return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); + } + } + fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { const is_volatile = false; // TODO if (!is_volatile and self.liveness.isUnused(inst)) @@ -1325,6 +1357,15 @@ pub const FuncGen = struct { return self.builder.buildStructGEP(struct_ptr, field_index, ""); } + fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const struct_ptr = try self.resolveInst(ty_op.operand); + return self.builder.buildStructGEP(struct_ptr, field_index, ""); + } + fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; @@ -1739,6 +1780,41 @@ pub const FuncGen = struct { return self.builder.buildXor(lhs, rhs, ""); } + fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_type = self.air.typeOf(bin_op.lhs); + const tg = self.dg.module.getTarget(); + const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + else + rhs; + return self.builder.buildShl(lhs, casted_rhs, ""); + } + + fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_type = self.air.typeOf(bin_op.lhs); + const tg = self.dg.module.getTarget(); + const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + else + rhs; + + if (self.air.typeOfIndex(inst).isSignedInt()) { + return self.builder.buildAShr(lhs, casted_rhs, ""); + } else { + return self.builder.buildLShr(lhs, casted_rhs, ""); + } + } + fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index fc97dfb81f..fe0c211df3 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -291,6 +291,14 @@ pub const Builder = opaque { pub const getInsertBlock = LLVMGetInsertBlock; extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock; + pub const buildZExt = LLVMBuildZExt; + extern fn LLVMBuildZExt( + *const Builder, + Value: *const Value, + DestTy: *const Type, + Name: [*:0]const u8, + ) *const Value; + pub const buildCall = LLVMBuildCall; extern fn LLVMBuildCall( *const Builder, @@ -382,6 +390,15 @@ pub const Builder = opaque { pub const buildAnd = LLVMBuildAnd; extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildLShr = LLVMBuildLShr; + extern fn LLVMBuildLShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildAShr = LLVMBuildAShr; + extern fn LLVMBuildAShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildShl = LLVMBuildShl; + extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildOr = LLVMBuildOr; extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 4814ba0b55..bb05567236 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -862,6 +862,10 @@ pub const Context = struct { .ret => self.airRet(inst), .store => self.airStore(inst), .struct_field_ptr => self.airStructFieldPtr(inst), + .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0), + .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1), + .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2), + .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3), .switch_br => self.airSwitchBr(inst), .unreach => self.airUnreachable(inst), .wrap_optional => self.airWrapOptional(inst), @@ -1198,7 +1202,12 @@ pub const Context = struct { // When constant has value 'null', set is_null local to '1' // and payload to '0' - if (val.tag() == .null_value) { + if (val.castTag(.opt_payload)) |pl| { + const payload_val = pl.data; + try writer.writeByte(wasm.opcode(.i32_const)); + try leb.writeILEB128(writer, @as(i32, 0)); + try self.emitConstant(payload_val, payload_type); + } else { try writer.writeByte(wasm.opcode(.i32_const)); try leb.writeILEB128(writer, @as(i32, 1)); @@ -1208,10 +1217,6 @@ pub const Context = struct { }); try writer.writeByte(wasm.opcode(opcode)); try leb.writeULEB128(writer, @as(u32, 0)); - } else { - try writer.writeByte(wasm.opcode(.i32_const)); - try leb.writeILEB128(writer, @as(i32, 0)); - try self.emitConstant(val, payload_type); } }, else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), @@ -1440,8 +1445,15 @@ pub const Context = struct { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.StructField, ty_pl.payload); const struct_ptr = self.resolveInst(extra.data.struct_operand); - - return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) }; + return structFieldPtr(struct_ptr, extra.data.field_index); + } + fn airStructFieldPtrIndex(self: *Context, inst: Air.Inst.Index, index: u32) InnerError!WValue { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const struct_ptr = self.resolveInst(ty_op.operand); + return structFieldPtr(struct_ptr, index); + } + fn structFieldPtr(struct_ptr: WValue, index: u32) InnerError!WValue { + return WValue{ .local = struct_ptr.multi_value.index + index }; } fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
