zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob e43397fa (14211B) - Raw


      1 const std = @import("std");
      2 const Value = @import("value.zig").Value;
      3 const Type = @import("type.zig").Type;
      4 const Module = @import("Module.zig");
      5 const assert = std.debug.assert;
      6 const codegen = @import("codegen.zig");
      7 const ast = std.zig.ast;
      8 
      9 /// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
     10 /// of instructions that correspond to the ZIR text format.
     11 /// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
     12 /// so are the `Value` and `Type`. The value of a constant must be copied into
     13 /// a memory location for the value to survive after a const instruction.
     14 pub const Inst = struct {
     15     tag: Tag,
     16     /// Each bit represents the index of an `Inst` parameter in the `args` field.
     17     /// If a bit is set, it marks the end of the lifetime of the corresponding
     18     /// instruction parameter. For example, 0b101 means that the first and
     19     /// third `Inst` parameters' lifetimes end after this instruction, and will
     20     /// not have any more following references.
     21     /// The most significant bit being set means that the instruction itself is
     22     /// never referenced, in other words its lifetime ends as soon as it finishes.
     23     /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
     24     /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
     25     /// lifetimes of operands are encoded elsewhere.
     26     deaths: DeathsInt = undefined,
     27     ty: Type,
     28     /// Byte offset into the source.
     29     src: usize,
     30 
     31     pub const DeathsInt = u16;
     32     pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
     33     pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
     34     pub const deaths_bits = unreferenced_bit_index - 1;
     35 
     36     pub fn isUnused(self: Inst) bool {
     37         return (self.deaths & (1 << unreferenced_bit_index)) != 0;
     38     }
     39 
     40     pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
     41         assert(index < deaths_bits);
     42         return @truncate(u1, self.deaths >> index) != 0;
     43     }
     44 
     45     pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
     46         assert(index < deaths_bits);
     47         self.deaths &= ~(@as(DeathsInt, 1) << index);
     48     }
     49 
     50     pub fn specialOperandDeaths(self: Inst) bool {
     51         return (self.deaths & (1 << deaths_bits)) != 0;
     52     }
     53 
     54     pub const Tag = enum {
     55         add,
     56         alloc,
     57         arg,
     58         assembly,
     59         bitand,
     60         bitcast,
     61         bitor,
     62         block,
     63         br,
     64         breakpoint,
     65         brvoid,
     66         call,
     67         cmp_lt,
     68         cmp_lte,
     69         cmp_eq,
     70         cmp_gte,
     71         cmp_gt,
     72         cmp_neq,
     73         condbr,
     74         constant,
     75         dbg_stmt,
     76         isnonnull,
     77         isnull,
     78         iserr,
     79         booland,
     80         boolor,
     81         /// Read a value from a pointer.
     82         load,
     83         loop,
     84         ptrtoint,
     85         ref,
     86         ret,
     87         retvoid,
     88         varptr,
     89         /// Write a value to a pointer. LHS is pointer, RHS is value.
     90         store,
     91         sub,
     92         unreach,
     93         not,
     94         floatcast,
     95         intcast,
     96         unwrap_optional,
     97         wrap_optional,
     98         xor,
     99         switchbr,
    100 
    101         pub fn Type(tag: Tag) type {
    102             return switch (tag) {
    103                 .alloc,
    104                 .retvoid,
    105                 .unreach,
    106                 .breakpoint,
    107                 .dbg_stmt,
    108                 => NoOp,
    109 
    110                 .ref,
    111                 .ret,
    112                 .bitcast,
    113                 .not,
    114                 .isnonnull,
    115                 .isnull,
    116                 .iserr,
    117                 .ptrtoint,
    118                 .floatcast,
    119                 .intcast,
    120                 .load,
    121                 .unwrap_optional,
    122                 .wrap_optional,
    123                 => UnOp,
    124 
    125                 .add,
    126                 .sub,
    127                 .cmp_lt,
    128                 .cmp_lte,
    129                 .cmp_eq,
    130                 .cmp_gte,
    131                 .cmp_gt,
    132                 .cmp_neq,
    133                 .store,
    134                 .booland,
    135                 .boolor,
    136                 .bitand,
    137                 .bitor,
    138                 .xor,
    139                 => BinOp,
    140 
    141                 .arg => Arg,
    142                 .assembly => Assembly,
    143                 .block => Block,
    144                 .br => Br,
    145                 .brvoid => BrVoid,
    146                 .call => Call,
    147                 .condbr => CondBr,
    148                 .constant => Constant,
    149                 .loop => Loop,
    150                 .varptr => VarPtr,
    151                 .switchbr => SwitchBr,
    152             };
    153         }
    154 
    155         pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
    156             return switch (op) {
    157                 .lt => .cmp_lt,
    158                 .lte => .cmp_lte,
    159                 .eq => .cmp_eq,
    160                 .gte => .cmp_gte,
    161                 .gt => .cmp_gt,
    162                 .neq => .cmp_neq,
    163             };
    164         }
    165     };
    166 
    167     /// Prefer `castTag` to this.
    168     pub fn cast(base: *Inst, comptime T: type) ?*T {
    169         if (@hasField(T, "base_tag")) {
    170             return base.castTag(T.base_tag);
    171         }
    172         inline for (@typeInfo(Tag).Enum.fields) |field| {
    173             const tag = @intToEnum(Tag, field.value);
    174             if (base.tag == tag) {
    175                 if (T == tag.Type()) {
    176                     return @fieldParentPtr(T, "base", base);
    177                 }
    178                 return null;
    179             }
    180         }
    181         unreachable;
    182     }
    183 
    184     pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
    185         if (base.tag == tag) {
    186             return @fieldParentPtr(tag.Type(), "base", base);
    187         }
    188         return null;
    189     }
    190 
    191     pub fn Args(comptime T: type) type {
    192         return std.meta.fieldInfo(T, .args).field_type;
    193     }
    194 
    195     /// Returns `null` if runtime-known.
    196     pub fn value(base: *Inst) ?Value {
    197         if (base.ty.onePossibleValue()) |opv| return opv;
    198 
    199         const inst = base.castTag(.constant) orelse return null;
    200         return inst.val;
    201     }
    202 
    203     pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
    204         return switch (base.tag) {
    205             .cmp_lt => .lt,
    206             .cmp_lte => .lte,
    207             .cmp_eq => .eq,
    208             .cmp_gte => .gte,
    209             .cmp_gt => .gt,
    210             .cmp_neq => .neq,
    211             else => null,
    212         };
    213     }
    214 
    215     pub fn operandCount(base: *Inst) usize {
    216         inline for (@typeInfo(Tag).Enum.fields) |field| {
    217             const tag = @intToEnum(Tag, field.value);
    218             if (tag == base.tag) {
    219                 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
    220             }
    221         }
    222         unreachable;
    223     }
    224 
    225     pub fn getOperand(base: *Inst, index: usize) ?*Inst {
    226         inline for (@typeInfo(Tag).Enum.fields) |field| {
    227             const tag = @intToEnum(Tag, field.value);
    228             if (tag == base.tag) {
    229                 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
    230             }
    231         }
    232         unreachable;
    233     }
    234 
    235     pub fn breakBlock(base: *Inst) ?*Block {
    236         return switch (base.tag) {
    237             .br => base.castTag(.br).?.block,
    238             .brvoid => base.castTag(.brvoid).?.block,
    239             else => null,
    240         };
    241     }
    242 
    243     pub const NoOp = struct {
    244         base: Inst,
    245 
    246         pub fn operandCount(self: *const NoOp) usize {
    247             return 0;
    248         }
    249         pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
    250             return null;
    251         }
    252     };
    253 
    254     pub const UnOp = struct {
    255         base: Inst,
    256         operand: *Inst,
    257 
    258         pub fn operandCount(self: *const UnOp) usize {
    259             return 1;
    260         }
    261         pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
    262             if (index == 0)
    263                 return self.operand;
    264             return null;
    265         }
    266     };
    267 
    268     pub const BinOp = struct {
    269         base: Inst,
    270         lhs: *Inst,
    271         rhs: *Inst,
    272 
    273         pub fn operandCount(self: *const BinOp) usize {
    274             return 2;
    275         }
    276         pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
    277             var i = index;
    278 
    279             if (i < 1)
    280                 return self.lhs;
    281             i -= 1;
    282 
    283             if (i < 1)
    284                 return self.rhs;
    285             i -= 1;
    286 
    287             return null;
    288         }
    289     };
    290 
    291     pub const Arg = struct {
    292         pub const base_tag = Tag.arg;
    293 
    294         base: Inst,
    295         name: [*:0]const u8,
    296 
    297         pub fn operandCount(self: *const Arg) usize {
    298             return 0;
    299         }
    300         pub fn getOperand(self: *const Arg, index: usize) ?*Inst {
    301             return null;
    302         }
    303     };
    304 
    305     pub const Assembly = struct {
    306         pub const base_tag = Tag.assembly;
    307 
    308         base: Inst,
    309         asm_source: []const u8,
    310         is_volatile: bool,
    311         output: ?[]const u8,
    312         inputs: []const []const u8,
    313         clobbers: []const []const u8,
    314         args: []const *Inst,
    315 
    316         pub fn operandCount(self: *const Assembly) usize {
    317             return self.args.len;
    318         }
    319         pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
    320             if (index < self.args.len)
    321                 return self.args[index];
    322             return null;
    323         }
    324     };
    325 
    326     pub const Block = struct {
    327         pub const base_tag = Tag.block;
    328 
    329         base: Inst,
    330         body: Body,
    331         /// This memory is reserved for codegen code to do whatever it needs to here.
    332         codegen: codegen.BlockData = .{},
    333 
    334         pub fn operandCount(self: *const Block) usize {
    335             return 0;
    336         }
    337         pub fn getOperand(self: *const Block, index: usize) ?*Inst {
    338             return null;
    339         }
    340     };
    341 
    342     pub const Br = struct {
    343         pub const base_tag = Tag.br;
    344 
    345         base: Inst,
    346         block: *Block,
    347         operand: *Inst,
    348 
    349         pub fn operandCount(self: *const Br) usize {
    350             return 0;
    351         }
    352         pub fn getOperand(self: *const Br, index: usize) ?*Inst {
    353             if (index == 0)
    354                 return self.operand;
    355             return null;
    356         }
    357     };
    358 
    359     pub const BrVoid = struct {
    360         pub const base_tag = Tag.brvoid;
    361 
    362         base: Inst,
    363         block: *Block,
    364 
    365         pub fn operandCount(self: *const BrVoid) usize {
    366             return 0;
    367         }
    368         pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
    369             return null;
    370         }
    371     };
    372 
    373     pub const Call = struct {
    374         pub const base_tag = Tag.call;
    375 
    376         base: Inst,
    377         func: *Inst,
    378         args: []const *Inst,
    379 
    380         pub fn operandCount(self: *const Call) usize {
    381             return self.args.len + 1;
    382         }
    383         pub fn getOperand(self: *const Call, index: usize) ?*Inst {
    384             var i = index;
    385 
    386             if (i < 1)
    387                 return self.func;
    388             i -= 1;
    389 
    390             if (i < self.args.len)
    391                 return self.args[i];
    392             i -= self.args.len;
    393 
    394             return null;
    395         }
    396     };
    397 
    398     pub const CondBr = struct {
    399         pub const base_tag = Tag.condbr;
    400 
    401         base: Inst,
    402         condition: *Inst,
    403         then_body: Body,
    404         else_body: Body,
    405         /// Set of instructions whose lifetimes end at the start of one of the branches.
    406         /// The `then` branch is first: `deaths[0..then_death_count]`.
    407         /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
    408         deaths: [*]*Inst = undefined,
    409         then_death_count: u32 = 0,
    410         else_death_count: u32 = 0,
    411 
    412         pub fn operandCount(self: *const CondBr) usize {
    413             return 1;
    414         }
    415         pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
    416             var i = index;
    417 
    418             if (i < 1)
    419                 return self.condition;
    420             i -= 1;
    421 
    422             return null;
    423         }
    424         pub fn thenDeaths(self: *const CondBr) []*Inst {
    425             return self.deaths[0..self.then_death_count];
    426         }
    427         pub fn elseDeaths(self: *const CondBr) []*Inst {
    428             return (self.deaths + self.then_death_count)[0..self.else_death_count];
    429         }
    430     };
    431 
    432     pub const Constant = struct {
    433         pub const base_tag = Tag.constant;
    434 
    435         base: Inst,
    436         val: Value,
    437 
    438         pub fn operandCount(self: *const Constant) usize {
    439             return 0;
    440         }
    441         pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
    442             return null;
    443         }
    444     };
    445 
    446     pub const Loop = struct {
    447         pub const base_tag = Tag.loop;
    448 
    449         base: Inst,
    450         body: Body,
    451 
    452         pub fn operandCount(self: *const Loop) usize {
    453             return 0;
    454         }
    455         pub fn getOperand(self: *const Loop, index: usize) ?*Inst {
    456             return null;
    457         }
    458     };
    459 
    460     pub const VarPtr = struct {
    461         pub const base_tag = Tag.varptr;
    462 
    463         base: Inst,
    464         variable: *Module.Var,
    465 
    466         pub fn operandCount(self: *const VarPtr) usize {
    467             return 0;
    468         }
    469         pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst {
    470             return null;
    471         }
    472     };
    473 
    474     pub const SwitchBr = struct {
    475         pub const base_tag = Tag.switchbr;
    476 
    477         base: Inst,
    478         target_ptr: *Inst,
    479         cases: []Case,
    480         /// Set of instructions whose lifetimes end at the start of one of the cases.
    481         /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
    482         deaths: [*]*Inst = undefined,
    483         else_index: u32 = 0,
    484         else_deaths: u32 = 0,
    485         else_body: Body,
    486 
    487         pub const Case = struct {
    488             item: Value,
    489             body: Body,
    490             index: u32 = 0,
    491             deaths: u32 = 0,
    492         };
    493 
    494         pub fn operandCount(self: *const SwitchBr) usize {
    495             return 1;
    496         }
    497         pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst {
    498             var i = index;
    499 
    500             if (i < 1)
    501                 return self.target_ptr;
    502             i -= 1;
    503 
    504             return null;
    505         }
    506         pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst {
    507             const case = self.cases[case_index];
    508             return (self.deaths + case.index)[0..case.deaths];
    509         }
    510         pub fn elseDeaths(self: *const SwitchBr) []*Inst {
    511             return (self.deaths + self.else_index)[0..self.else_deaths];
    512         }
    513     };
    514 };
    515 
    516 pub const Body = struct {
    517     instructions: []*Inst,
    518 };