Commit d44c9bdbd9

Andrew Kelley <andrew@ziglang.org>
2020-04-26 07:20:58
ir: elemptr and add instructions
1 parent 6481b02
Changed files (3)
src-self-hosted
src-self-hosted/ir/text.zig
@@ -32,6 +32,8 @@ pub const Inst = struct {
         fntype,
         intcast,
         bitcast,
+        elemptr,
+        add,
     };
 
     pub fn TagToType(tag: Tag) type {
@@ -50,6 +52,8 @@ pub const Inst = struct {
             .fntype => FnType,
             .intcast => IntCast,
             .bitcast => BitCast,
+            .elemptr => ElemPtr,
+            .add => Add,
         };
     }
 
@@ -157,6 +161,11 @@ pub const Inst = struct {
         },
         kw_args: struct {},
 
+        const Point = struct {
+            x: i32,
+            y: i32,
+        };
+
         pub const Body = struct {
             instructions: []*Inst,
         };
@@ -271,6 +280,28 @@ pub const Inst = struct {
         },
         kw_args: struct {},
     };
+
+    pub const ElemPtr = struct {
+        pub const base_tag = Tag.elemptr;
+        base: Inst,
+
+        positionals: struct {
+            array_ptr: *Inst,
+            index: *Inst,
+        },
+        kw_args: struct {},
+    };
+
+    pub const Add = struct {
+        pub const base_tag = Tag.add;
+        base: Inst,
+
+        positionals: struct {
+            lhs: *Inst,
+            rhs: *Inst,
+        },
+        kw_args: struct {},
+    };
 };
 
 pub const ErrorMsg = struct {
@@ -345,6 +376,8 @@ pub const Module = struct {
             .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
             .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
             .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
+            .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
+            .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
         }
     }
 
src-self-hosted/ir.zig
@@ -421,6 +421,8 @@ const Analyze = struct {
             .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
             .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
             .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?),
+            .elemptr => return self.analyzeInstElemPtr(func, old_inst.cast(text.Inst.ElemPtr).?),
+            .add => return self.analyzeInstAdd(func, old_inst.cast(text.Inst.Add).?),
         }
     }
 
@@ -569,6 +571,67 @@ const Analyze = struct {
         return self.bitcast(func, dest_type, operand);
     }
 
+    fn analyzeInstElemPtr(self: *Analyze, func: ?*Fn, inst: *text.Inst.ElemPtr) InnerError!*Inst {
+        const array_ptr = try self.resolveInst(func, inst.positionals.array_ptr);
+        const uncasted_index = try self.resolveInst(func, inst.positionals.index);
+        const elem_index = try self.coerce(func, Type.initTag(.usize), uncasted_index);
+
+        if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
+            if (array_ptr.value()) |array_ptr_val| {
+                if (elem_index.value()) |index_val| {
+                    // Both array pointer and index are compile-time known.
+                    const index_u64 = index_val.toUnsignedInt();
+                    // @intCast here because it would have been impossible to construct a value that
+                    // required a larger index.
+                    const elem_val = try array_ptr_val.elemValueAt(&self.arena.allocator, @intCast(usize, index_u64));
+
+                    const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal);
+                    ref_payload.* = .{ .val = elem_val };
+
+                    const type_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer);
+                    type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
+
+                    return self.constInst(inst.base.src, .{
+                        .ty = Type.initPayload(&type_payload.base),
+                        .val = Value.initPayload(&ref_payload.base),
+                    });
+                }
+            }
+        }
+
+        return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{});
+    }
+
+    fn analyzeInstAdd(self: *Analyze, func: ?*Fn, inst: *text.Inst.Add) InnerError!*Inst {
+        const lhs = try self.resolveInst(func, inst.positionals.lhs);
+        const rhs = try self.resolveInst(func, inst.positionals.rhs);
+
+        if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
+            if (lhs.value()) |lhs_val| {
+                if (rhs.value()) |rhs_val| {
+                    const lhs_bigint = try lhs_val.toBigInt(&self.arena.allocator);
+                    const rhs_bigint = try rhs_val.toBigInt(&self.arena.allocator);
+                    var result_bigint = try BigInt.init(&self.arena.allocator);
+                    try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint);
+
+                    if (!lhs.ty.eql(rhs.ty)) {
+                        return self.fail(inst.base.src, "TODO implement peer type resolution", .{});
+                    }
+
+                    const val_payload = try self.arena.allocator.create(Value.Payload.IntBig);
+                    val_payload.* = .{ .big_int = result_bigint };
+
+                    return self.constInst(inst.base.src, .{
+                        .ty = lhs.ty,
+                        .val = Value.initPayload(&val_payload.base),
+                    });
+                }
+            }
+        }
+
+        return self.fail(inst.base.src, "TODO implement more analyze add", .{});
+    }
+
     fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {
         const ptr = try self.resolveInst(func, deref.positionals.ptr);
         const elem_ty = switch (ptr.ty.zigTypeTag()) {
@@ -654,7 +717,22 @@ const Analyze = struct {
             return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
         }
 
-        return self.fail(inst.src, "TODO implement type coercion", .{});
+        // integer widening
+        if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
+            const src_info = inst.ty.intInfo(self.target);
+            const dst_info = dest_type.intInfo(self.target);
+            if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
+                if (inst.value()) |val| {
+                    return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
+                } else {
+                    return self.fail(inst.src, "TODO implement runtime integer widening", .{});
+                }
+            } else {
+                return self.fail(inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
+            }
+        }
+
+        return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
     }
 
     fn bitcast(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst {
src-self-hosted/value.zig
@@ -441,6 +441,61 @@ pub const Value = extern union {
         }
     }
 
+    /// Asserts the value is a single-item pointer to an array, or an array,
+    /// or an unknown-length pointer, and returns the element value at the index.
+    pub fn elemValueAt(self: Value, allocator: *Allocator, index: usize) Allocator.Error!Value {
+        switch (self.tag()) {
+            .ty,
+            .u8_type,
+            .i8_type,
+            .isize_type,
+            .usize_type,
+            .c_short_type,
+            .c_ushort_type,
+            .c_int_type,
+            .c_uint_type,
+            .c_long_type,
+            .c_ulong_type,
+            .c_longlong_type,
+            .c_ulonglong_type,
+            .c_longdouble_type,
+            .f16_type,
+            .f32_type,
+            .f64_type,
+            .f128_type,
+            .c_void_type,
+            .bool_type,
+            .void_type,
+            .type_type,
+            .anyerror_type,
+            .comptime_int_type,
+            .comptime_float_type,
+            .noreturn_type,
+            .fn_naked_noreturn_no_args_type,
+            .single_const_pointer_to_comptime_int_type,
+            .const_slice_u8_type,
+            .zero,
+            .void_value,
+            .noreturn_value,
+            .bool_true,
+            .bool_false,
+            .function,
+            .int_u64,
+            .int_i64,
+            .int_big,
+            => unreachable,
+
+            .ref => @panic("TODO figure out how MemoryCell works"),
+            .ref_val => @panic("TODO figure out how MemoryCell works"),
+
+            .bytes => {
+                const int_payload = try allocator.create(Value.Payload.Int_u64);
+                int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
+                return Value.initPayload(&int_payload.base);
+            },
+        }
+    }
+
     /// This type is not copyable since it may contain pointers to its inner data.
     pub const Payload = struct {
         tag: Tag,