# Location enum int VLOC_DATA = 2 int VLOC_STCK = 1 int VLOC_LITL = 0 # Should be -2 int32 PTYPE_NONE = 2 # Should be -1 int32 PTYPE_PTR = 1 int32 PTYPE_REF = 0 # 1 Arr is ptr to arr, larger #s are static size arrs int32 PTYPE_ARR = 1 ~uint8 PRIM_CSV_BOO = "bool\0" ~uint8 PRIM_CSV_INT = "int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64\0" ~uint8 PRIM_CSV_FLT = "float,float32,float64\0" # Should dispose of this constructed string # 1-8 are ax, bx, cx, dx, si, di, sp, bp # 9-16 are r8, r9, r10, r11, r12, r13, r14, r15 # 17-32 are xmm0, xmm1, xmm2, ..., xmm15 /; reg_string (int r, int size) [~uint8] utils.Vector out out.init(1) uint8 add /; if (r < 9) /; if (size == 4) add = 'e' out.push(~add) ;; else if (size == 8) add = 'r' out.push(~add) ;/ add = 'a' /; if (r < 5) add = add + r - 1 ;; else if (r == 5 || r == 7) add = 's' ;; else if (r == 6) add = 'd' ;; else if (r == 8) add = 'b' ;/ out.push(~add) /; if (r == 5 || r == 6) add = 'i' out.push(~add) ;; else if (r == 7 || r == 8) add = 'p' out.push(~add) ;; else if (size !== 1) add = 'x' out.push(~add) ;/ /; if (size == 1) add = 'l' out.push(~add) ;/ ;; else if (r < 17) add = 'r' out.push(~add) ~uint8 num = utils.int_to_str(r - 1) out.push_cstr(num) _delete(num) /; if (size == 1) add = 'b' out.push(~add) ;; else if (size == 2) add = 'w' out.push(~add) ;; else if (size == 4) add = 'd' out.push(~add) ;/ ;; else if (r < 33) out.push_cstr("xmm\0") ~uint8 num = utils.int_to_str(r - 17) out.push_cstr(num) _delete(num) ;/ return out.as_cstr() ;/ # Valid value states: # When loc is DATA value is in the data section and the name is actually the label # When loc is STCK value is offset from rbp pointer using the offset # When loc is 0 then offset represents a numeric literal # When loc is positive then the variable exists in that register* # Structs and references don't exactly exist in a register and so must # be combined with the offset to get a resonable approximation # So when computing the location of a standard type... # ...just load from the register # ...Unless it's a ref in which case take into account the offset # ...Or it's a DATA in which case load from rel label # ...Or it's on the stack in which case load from # ...Or it's a literal in which case just use the literal value # So when computing the location of a struct... # ...Load as an offset from a register # ...Unless it's on the stack (offset from rbp) # ...Or it's in DATA (offset from rel label) # ...Or it's a ref (lea first ref and then just load direct values from there) struct Var { ~uint8 name, ~Struct _type, utils.Vector ptrc, int loc, offset, ~parse.Node _tn, _id } /; method Var ########################### # Init and copy functions # ########################### # Initial init function, requires type node and # identifier node /; init (~parse.Node tn, id) self.name = utils.strcpy(id`.data) self.ptrc.init(4) self.loc = 0 self.offset = 0 self._tn = tn self._id = id ;/ # Deep copy the variable /; copy [Var] Var out = self.shallow_copy() /; loop (int i = 0; i < self.ptrc.count) [i++] ~int32 p = self.ptrc.get(i) out.ptrc.push(p) ;/ return out ;/ # A copy without the pointer chain /; shallow_copy [Var] Var out out.init(self._tn, self._id) out._type = self._type out.loc = self.loc out.offset = self.offset return out ;/ ############################# # Variable inspection funcs # ############################# # Get a pointer to the top of the pointer chain, returns # null if the pointer chain is empty /; top_ptrc [~int32] # Sanity /; if (self.ptrc.count < 1) return NULL ;/ ~int32 out = self.ptrc.get(self.ptrc.count - 1) return out ;/ # Returns true if the variable is a reference /; is_ref [bool] ~int32 p = self.top_ptrc() /; if (p == NULL) return false ;/ return p` == 0 ;/ # Returns true if two or more ref layers /; double_ref [bool] /; if (self.ptrc.count < 2) return false ;/ ~int32 p = self.ptrc.get(1) return p` == 0 ;/ # Returnes true if the underlying type is a signed integer /; is_signed [bool] /; if (_is_primitive(self._type`.name) !== 0) return self._type`.name{0} == 'i' ;/ return false ;/ # Returns true if the variable is a pointer /; is_ptr [bool] ~int32 p = self.top_ptrc() /; if (p == NULL) return false ;/ return p` < 0 ;/ # Returns true if the variable is an array /; is_arr [bool] ~int32 p = self.top_ptrc() /; if (p == NULL) return false ;/ return p` > 0 ;/ # Whether the variable can be stored within a register /; regable [bool] ~int p /; if (self.ptrc.count > 0) return true ;/ return _is_primitive(self._type`.name) !== 0 ;/ /; is_struct [bool] # Check first if we are a pointer of some sort ~int32 p /; loop (int i = 0; i < self.ptrc.count) [i++] p = self.ptrc.get(i) /; if (p` !== 0) return false ;/ ;/ return _is_primitive(self._type`.name) == 0 ;/ # Compute and add the correct pointer chain value for an array # type prefix /; _arr_ptr(~parse.Node a) int32 ptr = 1 /; if (a`.sub.count > 0) ~parse.Node l = a`.sub.get(0) ptr = utils.cstr_to_int(l`.data) /; if (ptr < 2) return ;/ ;/ self.ptrc.push(~ptr) ;/ # The "actual size" of the variable (if we were to do a mov on it # how much space would we need) /; actual_size [uint] /; if (self.ptrc.count > 0) return 8 ;; else if (self._type == NULL) return 0 ;/ return self._type`.size ;/ /; type_size [uint] /; loop (int i = 0; i < self.ptrc.count) [i++] ~int32 p = self.ptrc.get(i) /; if (p` !== 0) return 8 ;/ ;/ return self._type`.size ;/ ##################################### # Variable manipulation (comp time) # ##################################### # Reverse the pointer chain /; _reverse_ptrc int max = self.ptrc.count / 2 ~int32 l, r /; loop (int i = 0; i < max) [i++] l = self.ptrc.get(i) r = self.ptrc.get(self.ptrc.count - (i + 1)) int32 tmp = l` l` = r` r` = tmp ;/ ;/ # Sets up both the ptrc and the _type members, requires # parent module for resolution of types /; _resolve_type (~Module parent) int idx = 0 bool running = true ~parse.Node t, _tn _tn = self._tn # Pre-op pointer /; loop (running == true) /; if (idx !< _tn`.sub.count) running = false ;; else t = _tn`.sub.get(idx) /; if (t`._type == parse.NTYPE_PRE_OP) /; if (utils.strcmp(t`.data, "~\0") == true) int32 ptr = 0 ptr = ptr - PTYPE_PTR self.ptrc.push(~ptr) ;; else self._arr_ptr(t) ;/ ;; else running = false ;/ ;/ /; if (running == true) idx++ ;/ ;/ self._reverse_ptrc() # After pre-ops comes id utils.Vector strv strv.init(8) running = true /; loop (running == true) /; if (idx !< _tn`.sub.count) running = false ;; else t = _tn`.sub.get(idx) /; if (t`._type == parse.NTYPE_ID) ~uint8 str = t`.data strv.push(~str) ;; else running = false ;/ ;/ /; if (running == true) idx++ ;/ ;/ # Main type resolution # TODO: FUNCTION POINTER self._type = parent`.find(SEARCH_STRUCT, ~strv) strv.end() # Post-op pointer running = true /; loop (running == true) /; if (idx !< _tn`.sub.count) running = false ;; else t = _tn`.sub.get(idx) /; if (t`._type == parse.NTYPE_POST_OP) int32 ptr = 0 self.ptrc.push(~ptr) ;/ ;/ /; if (running == true) idx++ ;/ ;/ ;/ # Compile the variable into the data section /; _static_compile (~Module parent, ~CompBuf buf) # TODO: everything ;/ /; ptr_push (int32 p) self.ptrc.push(~p) ;/ /; ptr_pop self.ptrc.pop() ;/ /; end _delete(self.name) self.ptrc.end() ;/ #################################### # Variable manipulation (run time) # #################################### /; gen_loc [~uint8] /; if (self.loc == 0) return utils.int_to_str(self.offset) ;/ utils.Vector out out.init(1) /; if (self.in_mem() == true) out.push_char('[') ;/ ~uint8 str /; if (self.loc + 1 < 0) out.push_cstr("rel \0") str = utils.strcpy(self.name) ;; else if (self.loc < 0) str = reg_string(8, 8) ;; else str = reg_string(self.loc, self.actual_size()) ;/ out.push_cstr(str) _delete(str) /; if (self.in_mem() == true) /; if (self.loc + 1 == 0) int stk = 0 - self.offset /; if (stk > 0) out.push_cstr(" - \0") str = utils.int_to_str(stk) out.push_cstr(str) _delete(str) ;; else if (stk < 0) out.push_cstr(" + \0") str = utils.int_to_str(self.offset) out.push_cstr(str) _delete(str) ;/ ;/ out.push_char(']') ;/ return out.as_cstr() ;/ # Returns true if the variable is known to be stored in memory /; in_mem [bool] /; if (self.loc < 0) return true ;/ ~int32 ptr = self.top_ptrc() /; if (ptr !== NULL) /; if (ptr` == 0) return true ;; else if (ptr` > 1) return true ;/ ;/ return false ;/ # Typechecking # Typechecking structs /; _tc_struct (~Var other) [bool] /; if (other`.is_struct() == false) return false ;/ ~void a = self._type ~void b = other`._type return a == b ;/ /; _tc_prim (~Var other) [bool] # Allow implicit ptr conversions /; if (self.is_ptr() == true) return other`.is_ptr() ;/ return false ;/ # Operations # Helper to gen register when setting a struct /; _set_struct_r (~CompBuf buf, int reg) ~uint8 r = reg_string(reg, 8) buf`.add_c(" ; putting struct address into register\n\0") # Initial deref or move /; if (self.ptrc.count > 0) buf`.add_c(" mov \0") ;; else buf`.add_c(" lea \0") ;/ buf`.add_c(r) buf`.add_c(", [\0") # Reg, stack, or data /; if (self.loc + VLOC_DATA == 0) buf`.add_c("rel \0") buf`.add_c(self.name) ;; else if (self.loc + VLOC_STCK == 0) ~uint8 get_reg = reg_string(8, 8) buf`.add_c(get_reg) _delete(get_reg) ;; else ~uint8 get_reg = reg_string(self.loc, 8) buf`.add_c(get_reg) _delete(get_reg) ;/ # Deal with offset /; if (self.offset !== 0) int o = self.offset /; if (o < 0) o = 0 - o buf`.add_c(" - \0") ;; else buf`.add_c(" + \0") ;/ ;/ buf`.add_c("] ; initial struct addr move\n\0") # For as many more times as there are references /; loop (int i = 1; i < self.ptrc.count) [i++] buf`.add_c(" mov \0") buf`.add_c(r) buf`.add_c(", [\0") buf`.add_c(r) buf`.add_c("] ; reference chain\n\0") ;/ _delete(r) ;/ # Helper to properly move a struct in memory /; _set_struct(~CompBuf buf, ~Var other) # Typecheck /; if (self._tc_struct(other) == false) _printf("ERROR: Types do not match when setting struct. [\0") _printf(self._type`.name) _printf("] !== [\0") _printf(other`._type`.name) _printf("]\n\0") return ;/ # Have to get struct address (to set) into rdi self._set_struct_r(buf, 6) # Have to get struct address (to read) into rsi other`._set_struct_r(buf, 5) # Setup move size ~uint8 str str = utils.int_to_str(self._type`.size) buf`.add_c(" mov rcx, \0") buf`.add_c(str) buf`.add_c(" ; size of struct [\0") buf`.add_c(self._type`.name) buf`.add_c("] in bytes\n\0") _delete(str) # Move byte (rcx times) buf`.add_c(" rep movsb ; move struct\n\0") ;/ # Helper to properly get the lhs of a set /; _set_prim_l (~CompBuf buf) [~uint8] # Base of address/register ~uint8 out /; if (self.loc > 0) /; if (self.in_mem() == true) out = reg_string(self.loc, 8) ;; else uint sz = self.type_size() out = reg_string(self.loc, sz) ;/ ;; else utils.Vector vout /; if (self.loc + 1 == 0) # Stack vout.from_cstr("rbp \0") ;; else vout.from_cstr("rel \0") vout.push_cstr(self.name) ;/ int off = self.offset /; if (off < 0) off = 0 - off vout.push_cstr("- \0") ;; else if (off > 0) vout.push_cstr("+ \0") ;/ /; if (off !== 0) out = utils.int_to_str(off) vout.push_cstr(out) _delete(out) ;/ out = vout.as_cstr() /; if (self.is_ref() == true) # Need to move into rdi buf`.add_c(" mov rdi, [\0") buf`.add_c(out) buf`.add_c("]\n\0") _delete(out) out = utils.strcpy("rdi\0") ;/ ;/ # If in memory we need to wrap in [] /; if (self.in_mem() == false) utils.Vector vout vout.from_cstr("[\0") vout.push_cstr(out) vout.push_cstr("]\0") _delete(out) out = vout.as_cstr() ;/ # Loop and make sure we are dereferencing properly /; loop (int i = 1; i < self.ptrc.count) [i++] ~int pc = self.ptrc.get(i) /; if (pc` !== 0) i = self.ptrc.count ;; else buf`.add_c(" mov rdi, [rdi] ; auto deref\n\0") ;/ ;/ /; if (self.double_ref() == true) _delete(out) out = utils.strcpy("[rdi]\0") ;/ return out ;/ # Helper to properly get the rhs of a set /; _set_prim_r (~CompBuf buf, ~Var lhs) [~uint8] ~uint8 out = self._set_prim_l(buf) # Sign extend if required bool ext = false uint R = self.type_size() uint L = lhs`.type_size() /; if (R < L) ext = true ~uint8 vout = reg_string(5, L) /; if (lhs`.is_signed() == true && self.is_signed() == true) /; if (R < 4) buf`.add_c(" movsx \0") ;; else buf`.add_c(" movsxd \0") ;/ buf`.add_c(vout) buf`.add_c(", \0") ;; else if (R < 4) buf`.add_c(" movzx \0") buf`.add_c(vout) buf`.add_c(", \0") ;; else buf`.add_c(" mov \0") buf`.add_c(vout) buf`.add_c(", \0") ;/ buf`.add_c(out) buf`.add_c("\n\0") _delete(out) out = vout ;/ /; if (ext == false) /; if (self.in_mem() == true && lhs`.in_mem() == true) ~uint8 vout = reg_string(5, R) buf`.add_c(" mov \0") buf`.add_c(vout) buf`.add_c(", \0") buf`.add_c(out) buf`.add_c("\n\0") _delete(out) out = vout ;/ ;/ return out ;/ # Set this Variable to the value of other /; set (~CompBuf buf, ~Var other) # Options: # - If builtin then move based on size (byte, word, dword, qword) # - If pointer then move qword # - If struct then move via rep movsb /; if (self.is_struct() == false) # Struct set self._set_struct(buf, other) return ;/ # Generate lhs set and rhs set ~uint8 sr = other`._set_prim_r(buf, ~self) ~uint8 sl = self._set_prim_l(buf) buf`.add_c(" mov\0") buf`.add_c(sl) buf`.add_c(", \0") buf`.add_c(sr) buf`.add_c("\n\0") _delete(sl) _delete(sr) ;/ # Set the address which this reference points to /; set_ref (~CompBuf buf, ~Var other) ;/ # Generate a variable which can actually be used for operations /; strip_refs (~CompBuf buf, bool from) [Var] Var out = self.copy() ~int32 p = out.top_ptrc() /; if (p == NULL) return out ;; else if (p` !== 0) return out ;/ ~uint8 gen = out.gen_loc() out.ptr_pop() p = out.top_ptrc() /; loop (p` == 0) buf`.add_c(" mov rsi, \0") buf`.add_c(gen) buf`.add_c("\n\0") out.loc = 5 _delete(gen) gen = out.gen_loc() out.ptr_pop() p = out.top_ptrc() ;/ /; if (from == false) buf`.add_c(" mov rdi, \0") out.loc = 6 ;; else buf`.add_c(" mov rsi, \0") out.loc = 5 ;/ buf`.add_c(gen) buf`.add_c("\n\0") _delete(gen) return out ;/ /; standard_op (~CompBuf buf, ~Var other, ~uint8 op_str) ~uint8 to_str = self.gen_loc() ~uint8 from_str = other`.gen_loc() buf`.add_c(" \0") buf`.add_c(op_str) buf`.add_c(" \0") buf`.add_c(to_str) buf`.add_c(", \0") buf`.add_c(from_str) buf`.add_c("\n\0") _delete(from_str) _delete(to_str) ;/ /; product_op (~CompBuf buf, ~Var other, ~uint8 op_str, int read_reg) Var cpy = self.copy() cpy.loc = 1 cpy.offset = 0 # Set RAX register for product operation cpy.set(buf, ~self) ~uint8 from_str = other`.gen_loc() buf`.add_c(" \0") buf`.add_c(op_str) buf`.add_c(" \0") buf`.add_c(from_str) buf`.add_c("\n\0") _delete(from_str) # Set back the var from the read_reg cpy.loc = read_reg self.set(buf, cpy) cpy.end() ;/ /; add (~CompBuf buf, ~Var other) /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) self.offset = self.offset + other`.offset return ;/ self.standard_op(buf, other, "add") ;/ /; sub (~CompBuf buf, ~Var other) /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) self.offset = self.offset - other`.offset return ;/ self.standard_op(buf, other, "sub") ;/ /; mul (~CompBuf buf, ~Var other) /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) self.offset = self.offset * other`.offset return ;/ /; if (self.name{0} == 'u') self.product_op(buf, other, "mul", 1) ;; else self.product_op(buf, other, "imul", 1) ;/ ;/ /; div (~CompBuf buf, ~Var other) /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) self.offset = self.offset / other`.offset return ;/ /; if (self.name{0} == 'u') self.product_op(buf, other, "div", 1) ;; else self.product_op(buf, other, "idiv", 1) ;/ ;/ /; mod (~CompBuf buf, ~Var other) /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) self.offset = self.offset % other`.offset return ;/ /; if (self.name{0} == 'u') self.product_op(buf, other, "div", 4) ;; else self.product_op(buf, other, "idiv", 4) ;/ ;/ /; and (~CompBuf buf, ~Var other) self.standard_op(buf, other, "and") ;/ /; or (~CompBuf buf, ~Var other) self.standard_op(buf, other, "or") ;/ /; xor (~CompBuf buf, ~Var other) self.standard_op(buf, other, "xor") ;/ /; not (~CompBuf buf) ~uint8 to_str = self.gen_loc() buf`.add_c(" not \0") buf`.add_c(to_str) buf`.add_c("\n\0") _delete(to_str) ;/ /; member (~CompBuf buf, ~uint8 name) [Var] Var out return out ;/ # Printing /; _print (int idt) _indent(idt) _printf("{ Var : \0") _printf(self.name) _printf("\n\0") _indent(idt + 1) _printf("type: \0") /; if (self._type !== NULL) _printf(self._type`.name) ;; else _printf("(nil)\0") ;/ _printf("\n\0") _indent(idt + 1) _printf("ptrc: \0") ~int32 istr /; loop (int i = 0; i < self.ptrc.count) [i++] istr = self.ptrc.get(i) _print_num("%d \0", istr`) ;/ _printf("\n\0") _indent(idt + 1) _print_num("loc: %d\n\0", self.loc) _indent(idt + 1) _print_num("off: %d\n\0", self.offset) _indent(idt) _printf("}\n\0") ;/ ;/