| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #define LOG_DOMAIN "vm.helpers" |
| #include "cxxlog.h" |
| |
| #include "lil.h" |
| #include "open/vm_type_access.h" |
| #include "open/vm_method_access.h" |
| #include "nogc.h" |
| |
| // Forward decs of local functions |
| |
| static LilInstruction* lil_find_label(LilCodeStub*, LilLabel); |
| |
| //////////////////////////////////////////////////////////////////////////////////////// |
| // The LIL internal representation |
| |
| struct LilSig { |
| LilCc cc; |
| bool arbitrary; // If true then num_arg_types must be 0 |
| unsigned num_arg_types; // Must be 0 if arbitrary is true |
| LilType* arg_types; |
| enum LilType ret_type; |
| }; |
| |
| struct LilCond { |
| enum LilPredicate tag; |
| LilOperand o1, o2; |
| }; |
| |
| enum LilInstructionTag { |
| LIT_Label, LIT_Locals, LIT_StdPlaces, LIT_Alloc, LIT_Asgn, LIT_Ts, LIT_Handles, LIT_Ld, LIT_St, LIT_Inc, LIT_Cas, |
| LIT_J, LIT_Jc, LIT_Out, LIT_In2Out, LIT_Call, LIT_Ret, |
| LIT_PushM2N, LIT_M2NSaveAll, LIT_PopM2N, LIT_Print |
| }; |
| |
| struct LilInstruction { |
| enum LilInstructionTag tag; |
| union { |
| struct { |
| LilLabel l; |
| LilInstructionContext* ctxt; // Computed lazily |
| } label; |
| unsigned locals; |
| unsigned std_places; |
| struct { |
| LilVariable dst; |
| unsigned num_bytes; |
| } alloc; |
| struct { |
| enum LilOperation op; |
| LilVariable dst; |
| LilOperand o1, o2; |
| } asgn; |
| LilVariable ts; |
| LilOperand handles; |
| // Both ld, st, inc, & case use ldst |
| // operand is dst for ld, src for st, unused for inc, src for case |
| // cmp and l are used only for cas |
| // [base,scale,index,offset] is src for ld, dst for st |
| // base present iff is_base |
| // index present iff is_index |
| struct { |
| LilType t; |
| bool is_base, is_index; |
| LilVariable base, index; |
| LilOperand operand; |
| unsigned scale; |
| POINTER_SIZE_SINT offset; |
| LilAcqRel acqrel; |
| LilLdX extend; |
| LilOperand compare; |
| LilLabel l; |
| } ldst; |
| LilLabel j; |
| struct { |
| LilCond c; |
| LilLabel l; |
| } jc; |
| LilSig out; |
| struct { |
| LilCallKind k; |
| LilOperand target; |
| } call; |
| struct { |
| Method_Handle method; |
| frame_type current_frame_type; |
| bool handles; |
| } push_m2n; |
| struct { |
| char *str; |
| LilOperand arg; |
| } print; |
| } u; |
| struct LilInstruction* next; |
| }; |
| |
| |
| enum LilCodeStubContexts { LCSC_NotComputed, LCSC_Computed, LCSC_Error }; |
| |
| struct LilCodeStub { |
| tl::MemoryPool* my_memory; |
| unsigned cur_gen_label; |
| unsigned num_std_places; |
| LilSig sig; |
| LilInstruction* is; |
| LilCodeStubContexts ctxt_state; |
| unsigned num_is, max_std_places, max_locals; |
| LilInstructionContext* init_ctxt; // Computed lazily |
| |
| // size of generated code |
| size_t compiled_code_size; |
| |
| // a list of BBs in this code stub; null if the FG has not been initializsed; computed lazily |
| LilBb* bb_list_head; |
| unsigned num_bbs; |
| }; |
| |
| |
| |
| //*** Freers |
| |
| VMEXPORT void lil_free_code_stub(LilCodeStub* cs) |
| { |
| delete cs->my_memory; |
| } |
| |
| //*** Freers - end |
| |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| // The LIL Parser |
| |
| // The LIL parser is loosely designed as a scannerless recursive-descent parser. |
| // The various functions take a pointer to a source string pointer and update |
| // the pointer as they parse. Most functions are designed to advance the pointer |
| // only by token amounts. Functions that parse something and return a pointer |
| // to the structure, return NULL on failure. Other functions that parse directly |
| // a structure, take a pointer to the structure to parse into, and return a boolean |
| // to indicate success; the latter also include parsing primitive types. |
| // Parsing functions other than for code stubs do not cleanup allocated memory as |
| // this is arena deallocated in parse_code_stub. |
| |
| // Here are some character classes used in various parts of the parser |
| |
| #define alpha(c) (((c)>='a' && (c)<='z') || ((c)>='A' && (c)<='Z') || (c)=='_') |
| #define num(c) ((c)>='0' && (c) <='9') |
| #define hexnum(c) (num(c) || ((c)>='a' && (c)<='f') || ((c)>='A' && (c)<='F')) |
| #define alphanum(c) (alpha(c) || num(c)) |
| |
| static void error(const char** src, const char* err1, const char* err2="") |
| { |
| FILE* err_f = stderr; |
| fprintf(err_f, "lil parse error: %s%s\n\t", err1, err2); |
| unsigned count=0; |
| const char* s =*src; |
| while (s[0] && count++<70) { |
| fputc((s[0]<=' ' ? ' ' : s[0]), err_f); |
| s++; |
| } |
| fputc('\n', err_f); |
| fflush(err_f); |
| } |
| |
| // Skip over any white space |
| static void lil_skip_ws(const char** src) |
| { |
| while ((*src)[0]==' ' || (*src)[0]=='\n' || (*src)[0]=='\r' || (*src)[0]=='\t' || (*src)[0]=='/') { |
| if ((*src)[0]=='/') |
| if ((*src)[1]=='/') |
| while ((*src)[0] && (*src)[0]!='\n') ++*src; |
| else |
| return; |
| else |
| ++*src; |
| } |
| } |
| |
| struct LilVarArgs { |
| unsigned current; |
| va_list val; |
| }; |
| |
| static char lil_parse_percent(const char** src, LilVarArgs* va) |
| { |
| if ((*src)[0]!='%') { error(src, "expecting %", ""); return '\0'; } |
| unsigned i=1, v=0; |
| while (num((*src)[i])) |
| v = 10*v+((*src)[i++]-'0'); |
| if (v!=va->current) { error(src, "%s not numbered sequentially", ""); return '\0'; } |
| va->current++; |
| *src += i+1; |
| return (*src)[-1]; |
| } |
| |
| // Check and skip over a specific "keyword" |
| // This function does not change *src on failure |
| // (except to skip whitespace), |
| // and this is needed in some code below |
| static bool lil_parse_kw_no_error(const char** src, const char* kw) |
| { |
| lil_skip_ws(src); |
| unsigned c; |
| for (c=0; kw[c]; c++) |
| if ((*src)[c]!=kw[c]) return false; |
| // If the keyword could be an identifier or number then check that the source is |
| // not a longer identifier or number |
| if (alphanum(kw[c-1]) && alphanum((*src)[c])) return false; |
| *src += c; |
| return true; |
| } |
| |
| static bool lil_parse_kw(const char** src, const char* kw) |
| { |
| bool res = lil_parse_kw_no_error(src, kw); |
| if (!res) error(src, "expected ", kw); |
| return res; |
| } |
| |
| // Parse a calling convention |
| static bool lil_parse_cc(const char** src, LilCc* cc) |
| { |
| lil_skip_ws(src); |
| switch ((*src)[0]) { |
| case 'j': // jni |
| if (!lil_parse_kw(src, "jni")) return false; |
| *cc = LCC_Jni; |
| return true; |
| case 'm': // managed |
| if (!lil_parse_kw(src, "managed")) return false; |
| *cc = LCC_Managed; |
| return true; |
| case 'p': // platform |
| if (!lil_parse_kw(src, "platform")) return false; |
| *cc = LCC_Platform; |
| return true; |
| case 'r': // rth |
| if (!lil_parse_kw(src, "rth")) return false; |
| *cc = LCC_Rth; |
| return true; |
| case 's': // stdcall |
| if (!lil_parse_kw(src, "stdcall")) return false; |
| *cc = LCC_StdCall; |
| return true; |
| default: |
| error(src, "bad calling convention", ""); |
| return false; |
| } |
| } |
| |
| // Parse a type |
| // If ret is true then void is allowed otherwise not |
| static bool lil_parse_type(const char** src, LilType* t, bool ret) |
| { |
| lil_skip_ws(src); |
| switch ((*src)[0]) { |
| case 'f': // f4, f8 |
| if ((*src)[1]=='4') *t = LT_F4; |
| else if ((*src)[1]=='8') *t = LT_F8; |
| else { error(src, "expecting f4 or f8", ""); return false; } |
| if (alphanum((*src)[2])) { error(src, "expecting f4 or f8", ""); return false; } |
| *src += 2; |
| return true; |
| case 'g': // g1, g2, g4, g8 |
| if ((*src)[1]=='1') *t = LT_G1; |
| else if ((*src)[1]=='2') *t = LT_G2; |
| else if ((*src)[1]=='4') *t = LT_G4; |
| else if ((*src)[1]=='8') *t = LT_G8; |
| else { error(src, "expecting g1, g2, g4, or g8", ""); return false; } |
| if (alphanum((*src)[2])) { error(src, "expecting g1, g2, g4, or g8", ""); return false; } |
| *src += 2; |
| return true; |
| case 'r': // refmethod_get_name(meth) |
| if (!lil_parse_kw(src, "ref")) return false; |
| *t = LT_Ref; |
| return true; |
| case 'p': // pint |
| if (!lil_parse_kw(src, "pint")) return false; |
| *t = LT_PInt; |
| return true; |
| case 'v': // void |
| if (!ret) { error(src, "cannot have void type", ""); return false; } |
| if (!lil_parse_kw(src, "void")) return false; |
| *t = LT_Void; |
| return true; |
| default: |
| error(src, "bad type", ""); |
| return false; |
| } |
| } |
| |
| static LilType type_info_to_lil_type(Type_Info_Handle tih, bool handles) |
| { |
| if (type_info_is_managed_pointer(tih)) { |
| return LT_PInt; |
| } |
| VM_Data_Type dt = type_info_get_type(tih); |
| switch (dt) { |
| case VM_DATA_TYPE_INT8: |
| case VM_DATA_TYPE_UINT8: return LT_G1; |
| case VM_DATA_TYPE_INT16: |
| case VM_DATA_TYPE_UINT16: return LT_G2; |
| case VM_DATA_TYPE_INT32: |
| case VM_DATA_TYPE_UINT32: return LT_G4; |
| case VM_DATA_TYPE_INT64: |
| case VM_DATA_TYPE_UINT64: return LT_G8; |
| case VM_DATA_TYPE_INTPTR: |
| case VM_DATA_TYPE_UINTPTR: return LT_PInt; |
| case VM_DATA_TYPE_F8: return LT_F8; |
| case VM_DATA_TYPE_F4: return LT_F4; |
| case VM_DATA_TYPE_BOOLEAN: return LT_G1; |
| case VM_DATA_TYPE_CHAR: return LT_G2; |
| case VM_DATA_TYPE_CLASS: |
| case VM_DATA_TYPE_ARRAY: return (handles ? LT_PInt : LT_Ref); |
| case VM_DATA_TYPE_VOID: return LT_Void; |
| case VM_DATA_TYPE_VALUE:{ |
| // ? 20030613: I really don't want this code here, but for now... |
| Class_Handle UNUSED ch = type_info_get_class(tih); |
| assert(ch); |
| LDIE(52, "Unexpected data type"); |
| } |
| case VM_DATA_TYPE_MP: |
| case VM_DATA_TYPE_UP: |
| return LT_PInt; |
| case VM_DATA_TYPE_STRING: |
| case VM_DATA_TYPE_INVALID: |
| default: DIE(("Unknown data type")); for(;;); |
| } |
| } |
| |
| // Parse a signature (cc:Ts:RT) |
| // Currently this function can only handle a maximum number of |
| // argument types |
| #define MAX_ARG_TYPES 20 |
| static bool lil_parse_sig(const char** src, tl::MemoryPool* mem, LilVarArgs* va, LilSig* s) |
| { |
| if (!lil_parse_cc(src, &(s->cc))) return false; |
| if (!lil_parse_kw(src, ":")) return false; |
| lil_skip_ws(src); |
| if ((*src)[0]=='%') { |
| char specifier = lil_parse_percent(src, va); |
| bool jni; |
| switch (specifier) { |
| case 'm': |
| jni = false; |
| break; |
| case 'j': |
| jni = true; |
| break; |
| default: |
| if (specifier) error(src, "bad specifier for signature", ""); |
| return false; |
| } |
| Method_Handle m = va_arg(va->val, Method_Handle); |
| Method_Signature_Handle sig = method_get_signature(m); |
| unsigned num_method_args = method_args_get_number(sig); |
| unsigned num_args = num_method_args; |
| if (jni) { |
| num_args++; |
| if (method_is_static(m)) num_args++; |
| } |
| s->arbitrary = false; |
| s->num_arg_types = num_args; |
| s->arg_types = (LilType*)mem->alloc(num_args*sizeof(LilType)); |
| unsigned cur=0; |
| if (jni) { |
| s->arg_types[cur++] = LT_PInt; |
| if (method_is_static(m)) s->arg_types[cur++] = LT_PInt; |
| } |
| for(unsigned i=0; i<num_method_args; i++) |
| s->arg_types[cur++] = type_info_to_lil_type(method_args_get_type_info(sig, i), jni); |
| s->ret_type = type_info_to_lil_type(method_ret_type_get_type_info(sig), jni); |
| } else if ((*src)[0]=='a') { |
| if (!lil_parse_kw(src, "arbitrary")) return false; |
| s->arbitrary = true; |
| s->num_arg_types = 0; // This is important to the validity checks |
| s->arg_types = NULL; |
| s->ret_type = LT_Void; |
| } else { |
| s->arbitrary = false; |
| if ((*src)[0]!=':') { |
| LilType ts[MAX_ARG_TYPES]; |
| unsigned cur = 0; |
| for(;;) { |
| assert(cur<MAX_ARG_TYPES); |
| if (!lil_parse_type(src, ts+cur, false)) return false; |
| ++cur; |
| lil_skip_ws(src); |
| if ((*src)[0]==':') break; |
| else if ((*src)[0]!=',') { error(src, "expecting , or :", ""); return false; } |
| ++*src; |
| } |
| s->num_arg_types = cur; |
| s->arg_types = (LilType*)mem->alloc(cur*sizeof(LilType)); |
| for(unsigned i=0; i<cur; i++) s->arg_types[i] = ts[i]; |
| } else { |
| s->num_arg_types = 0; |
| s->arg_types = NULL; |
| } |
| if (!lil_parse_kw(src, ":")) return false; |
| if (!lil_parse_type(src, &(s->ret_type), true)) return false; |
| } |
| return true; |
| } |
| |
| // Parse an immediate string |
| static bool lil_parse_string(const char** src, char** str) |
| { |
| static char buffer[1000]; |
| lil_skip_ws(src); |
| if ((*src)[0] != '\'') |
| return false; |
| (*src)++; // skip opening quote |
| int len; |
| for (len=0; **src != '\'' && **src != '\0'; ++*src, ++len) { |
| if (len >= 1000) { |
| error(src, "string too long (more than 1000 chars)"); |
| return false; |
| } |
| buffer[len] = **src; |
| } |
| if (**src == '\0') { |
| error(src, "open string"); |
| return false; |
| } |
| // skip closing quote |
| ++*src; |
| buffer[len] = '\0'; |
| *str = (char *) malloc_fixed_code_for_jit(strlen(buffer) + 1, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_DEFAULT, CAA_Allocate); |
| strcpy(*str, buffer); |
| return true; |
| } |
| |
| |
| // Parse an immediate |
| static bool lil_parse_number(const char** src, LilVarArgs* va, POINTER_SIZE_INT* num) |
| { |
| lil_skip_ws(src); |
| if ((*src)[0]=='%') { |
| char specifier = lil_parse_percent(src, va); |
| if (specifier=='i') { |
| *num = va_arg(va->val, POINTER_SIZE_INT); |
| return true; |
| } |
| else { |
| if (specifier) error(src, "bad specifier for immediate", ""); |
| return false; |
| } |
| } else if ((*src)[0]=='0' && (*src)[1]=='x') { |
| *num=0; |
| unsigned c=2; |
| while (hexnum((*src)[c])) { |
| // NB the following statement is dependent upon ASCII ordering |
| *num = *num * 16 + ((*src)[c]>='a' ? (*src)[c]-'a'+10 : (*src)[c]>='A' ? (*src)[c]-'A'+10 : (*src)[c]-'0'); |
| c++; |
| } |
| if (c==2) { error(src, "0x must be followed by at least one hexidigit", ""); return false; } |
| *src += c; |
| return true; |
| } else { |
| *num=0; |
| unsigned c=0; |
| while (num((*src)[c])) { |
| *num = *num * 10 + (*src)[c]-'0'; |
| c++; |
| } |
| if (c==0) { error(src, "expecting number", ""); return false; } |
| *src += c; |
| return true; |
| } |
| } |
| |
| // Parse a label |
| static LilLabel lil_parse_label(const char** src, unsigned* cur_gen_label, tl::MemoryPool* mem) |
| { |
| lil_skip_ws(src); |
| if ((*src)[0]=='%') { |
| unsigned index; |
| switch ((*src)[1]) { |
| case 'g': |
| index = ++*cur_gen_label; |
| break; |
| case 'p': |
| index = *cur_gen_label; |
| break; |
| case 'n': |
| index = *cur_gen_label + 1; |
| break; |
| case 'o': |
| index = *cur_gen_label + 2; |
| break; |
| default: |
| error(src, "bad % specifier in label", ""); |
| return NULL; |
| } |
| char* buf = (char*)mem->alloc(17); |
| sprintf(buf, "$%x", index); |
| *src += 2; |
| return buf; |
| } |
| if (!alpha((*src)[0])) { error(src, "null label", ""); return NULL; } |
| unsigned c=1; |
| while (alphanum((*src)[c])) c++; |
| char* buf = (char*)mem->alloc(c+1); |
| for(unsigned i=0; i<c; i++) buf[i] = (*src)[i]; |
| buf[c] = '\0'; |
| *src += c; |
| return buf; |
| } |
| |
| // Parse a variable |
| static bool lil_parse_variable(const char** src, LilVarArgs* va, LilVariable* v) |
| { |
| unsigned start; |
| lil_skip_ws(src); |
| switch ((*src)[0]) { |
| case 'i': v->tag = LVK_In; start = 1; break; |
| case 'l': v->tag = LVK_Local; start = 1; break; |
| case 'o': v->tag = LVK_Out; start = 1; break; |
| case 's': |
| if ((*src)[1]!='p') { error(src, "expecting variable", ""); return false; } |
| v->tag = LVK_StdPlace; |
| start = 2; |
| break; |
| case 'r': |
| if (alphanum((*src)[1])) { error(src, "expecting variable", ""); return false; } |
| v->tag = LVK_Ret; |
| v->index = 0; |
| ++*src; |
| return true; |
| default: |
| error(src, "expecting variable", ""); |
| return false; |
| } |
| if ((*src)[start]=='%') { |
| *src += start; |
| char specifier = lil_parse_percent(src, va); |
| if (specifier=='i') { |
| v->index = va_arg(va->val, int); |
| return true; |
| } else { |
| if (specifier) error(src, "bad specifier for variable index", ""); |
| return false; |
| } |
| } else { |
| unsigned c=0; |
| v->index = 0; |
| while (num((*src)[start+c])) { |
| v->index = v->index*10 + (*src)[start+c]-'0'; |
| c++; |
| } |
| if (c==0 || alpha((*src)[start+c])) { error(src, "bad variable", ""); return false; } |
| *src += start+c; |
| return true; |
| } |
| } |
| |
| |
| // Parse an operand |
| static bool lil_parse_operand(const char** src, LilVarArgs* va, LilOperand* o) |
| { |
| lil_skip_ws(src); |
| if (((*src)[0]>='0' && (*src)[0]<='9') || (*src)[0]=='%') { |
| o->is_immed = true; |
| if (!lil_parse_number(src, va, &(o->val.imm))) return false; |
| } else { |
| o->is_immed = false; |
| if (!lil_parse_variable(src, va, &(o->val.var))) return false; |
| } |
| lil_skip_ws(src); |
| if ((*src)[0]==':') { |
| o->has_cast = true; |
| ++*src; |
| if (!lil_parse_type(src, &o->t, false)) return false; |
| } else { |
| o->has_cast = false; |
| } |
| return true; |
| } |
| |
| // Parse a condition |
| static bool lil_parse_cond(const char** src, LilVarArgs* va, LilCond* c) |
| { |
| if (!lil_parse_operand(src, va, &(c->o1))) return false; |
| lil_skip_ws(src); |
| if ((*src)[0]=='=') |
| c->tag = LP_Eq, ++*src; |
| else if ((*src)[0]=='!' && (*src)[1]=='=') |
| c->tag = LP_Ne, *src += 2; |
| else if ((*src)[0]=='<') |
| if ((*src)[1]=='=') |
| if ((*src)[2]=='u') |
| if (alphanum((*src)[3])) |
| { error(src, "expecting predicate", ""); return false; } |
| else |
| c->tag = LP_Ule, *src += 3; |
| else |
| c->tag = LP_Le, *src += 2; |
| else if ((*src)[1]=='u') |
| if (alphanum((*src)[2])) |
| { error(src, "expecting predicate", ""); return false; } |
| else |
| c->tag = LP_Ult, *src += 2; |
| else |
| c->tag = LP_Lt, ++*src; |
| else |
| { error(src, "expecting predicate", ""); return false; } |
| if (!lil_parse_operand(src, va, &(c->o2))) return false; |
| // Fixup some special cases |
| if (!c->o1.is_immed && c->o2.is_immed && c->o2.val.imm==0) |
| if (c->tag==LP_Eq) |
| c->tag = LP_IsZero; |
| else if (c->tag==LP_Ne) |
| c->tag = LP_IsNonzero; |
| return true; |
| } |
| |
| static bool lil_parse_plusminus(const char** src, int* sign) |
| { |
| lil_skip_ws(src); |
| if ((*src)[0]=='+') { |
| *sign = +1; |
| ++*src; |
| return true; |
| } else if ((*src)[0]=='-') { |
| *sign = -1; |
| ++*src; |
| return true; |
| } else { |
| error(src, "expecting + or -", ""); |
| return false; |
| } |
| } |
| |
| // Parse an address (part of load or store) |
| static bool lil_parse_address(const char** src, LilVarArgs* va, LilInstruction* i) |
| { |
| int sign = +1; |
| if (!lil_parse_kw(src, "[")) return false; |
| lil_skip_ws(src); |
| if (alpha((*src)[0])) { |
| if (!lil_parse_variable(src, va, &(i->u.ldst.base))) return false; |
| if (!lil_parse_plusminus(src, &sign)) return false; |
| i->u.ldst.is_base = true; |
| } else { |
| i->u.ldst.is_base = false; |
| } |
| // Parse a number, this could be the scale or the immediate, |
| // which will be determined by what follows |
| POINTER_SIZE_INT n; |
| if (!lil_parse_number(src, va, &n)) return false; |
| lil_skip_ws(src); |
| if (lil_parse_kw_no_error(src, "*")) { |
| if (sign<0) { error(src, "cannot subtract scaled index", ""); return false; } |
| i->u.ldst.is_index = true; |
| i->u.ldst.scale = (unsigned) n; |
| if (!lil_parse_variable(src, va, &(i->u.ldst.index))) return false; |
| if (!lil_parse_plusminus(src, &sign)) return false; |
| if (!lil_parse_number(src, va, &n)) return false; |
| } else { |
| i->u.ldst.is_index = false; |
| i->u.ldst.scale = 0; |
| } |
| i->u.ldst.offset = sign * (POINTER_SIZE_SINT) n; |
| if (!lil_parse_kw(src, ":")) return false; |
| if (!lil_parse_type(src, &i->u.ldst.t, false)) return false; |
| if (lil_parse_kw_no_error(src, ",")) { |
| if (lil_parse_kw_no_error(src, "acquire")) { |
| i->u.ldst.acqrel = LAR_Acquire; |
| } else if (lil_parse_kw_no_error(src, "release")) { |
| i->u.ldst.acqrel = LAR_Release; |
| } else { |
| error(src, "expecting acquire or release", ""); |
| return false; |
| } |
| } else { |
| i->u.ldst.acqrel = LAR_None; |
| } |
| if (!lil_parse_kw(src, "]")) return false; |
| return true; |
| } |
| |
| static bool lil_parse_load_extend(const char** src, LilInstruction* i) |
| { |
| if (lil_parse_kw_no_error(src, ",")) { |
| if (lil_parse_kw_no_error(src, "sx")) { |
| i->u.ldst.extend = LLX_Sign; |
| } else if (lil_parse_kw_no_error(src, "zx")) { |
| i->u.ldst.extend = LLX_Zero; |
| } else { |
| error(src, "expecting sx or zx", ""); |
| return false; |
| } |
| } else { |
| i->u.ldst.extend = LLX_None; |
| } |
| return true; |
| } |
| |
| // Parse an instruction |
| static LilInstruction* lil_parse_instruction(const char** src, tl::MemoryPool* mem, unsigned* cgl, LilVarArgs* va, LilSig* entry_sig) |
| { |
| LilInstruction* i = (LilInstruction*)mem->alloc(sizeof(LilInstruction)); |
| lil_skip_ws(src); |
| // Look ahead at characters until the instruction form is determined |
| // Then parse that form |
| // Not the most maintainable code, but it keeps the design simple for now |
| switch ((*src)[0]) { |
| case ':': // :label |
| ++*src; |
| i->tag = LIT_Label; |
| i->u.label.l = lil_parse_label(src, cgl, mem); |
| if (!i->u.label.l) return NULL; |
| i->u.label.ctxt = NULL; |
| break; |
| case 'a': // alloc |
| { |
| i->tag = LIT_Alloc; |
| if (!lil_parse_kw(src, "alloc")) return NULL; |
| if (!lil_parse_variable(src, va, &(i->u.alloc.dst))) return NULL; |
| if (!lil_parse_kw(src, ",")) return NULL; |
| POINTER_SIZE_INT n; |
| if (!lil_parse_number(src, va, &n)) return NULL; |
| i->u.alloc.num_bytes = (unsigned) n; |
| break; |
| } |
| case 'c': // call, call.noret, cas |
| if ((*src)[1] && (*src)[2]=='s') { |
| i->tag = LIT_Cas; |
| if (!lil_parse_kw(src, "cas")) return NULL; |
| if (!lil_parse_address(src, va, i)) return NULL; |
| if (!lil_parse_kw(src, "=")) return NULL; |
| if (!lil_parse_operand(src, va, &i->u.ldst.compare)) return NULL; |
| if (!lil_parse_kw(src, ",")) return NULL; |
| if (!lil_parse_operand(src, va, &i->u.ldst.operand)) return NULL; |
| if (!lil_parse_kw(src, ",")) return NULL; |
| i->u.ldst.l = lil_parse_label(src, cgl, mem); |
| if (!i->u.ldst.l) return NULL; |
| break; |
| } else if ((*src)[1] && (*src)[2] && (*src)[3] && (*src)[4]=='.') { |
| i->tag = LIT_Call; |
| if (!lil_parse_kw(src, "call.noret")) return NULL; |
| i->u.call.k = LCK_CallNoRet; |
| } else { |
| i->tag = LIT_Call; |
| if (!lil_parse_kw(src, "call")) return NULL; |
| i->u.call.k = LCK_Call; |
| } |
| if (!lil_parse_operand(src, va, &(i->u.call.target))) return NULL; |
| break; |
| case 'h': // handles |
| i->tag = LIT_Handles; |
| if (!lil_parse_kw(src, "handles")) return NULL; |
| if (!lil_parse_kw(src, "=")) return NULL; |
| if (!lil_parse_operand(src, va, &(i->u.handles))) return NULL; |
| break; |
| case 'i': // =, in2out, inc |
| if (num((*src)[1]) || (*src)[1]=='%') goto asgn; |
| // in2out, inc |
| if ((*src)[1]=='n' && (*src)[2]=='c') { |
| // inc |
| i->tag = LIT_Inc; |
| if (!lil_parse_kw(src, "inc")) return NULL; |
| if (!lil_parse_address(src, va, i)) return NULL; |
| } else { |
| // in2out |
| if (entry_sig->arbitrary) { error(src, "in2out in an arbitrary code stub", ""); return NULL; } |
| i->tag = LIT_In2Out; |
| if (!lil_parse_kw(src, "in2out")) return NULL; |
| if (!lil_parse_cc(src, &(i->u.out.cc))) return NULL; |
| i->u.out.arbitrary = false; |
| i->u.out.num_arg_types = entry_sig->num_arg_types; |
| i->u.out.arg_types = entry_sig->arg_types; |
| if (!lil_parse_kw(src, ":")) return NULL; |
| if (!lil_parse_type(src, &(i->u.out.ret_type), true)) return NULL; |
| } |
| break; |
| case 'j': // j, jc |
| if ((*src)[1]=='c') { // jc |
| i->tag = LIT_Jc; |
| if (!lil_parse_kw(src, "jc")) return NULL; |
| if (!lil_parse_cond(src, va, &(i->u.jc.c))) return NULL; |
| if (!lil_parse_kw(src, ",")) return NULL; |
| i->u.jc.l = lil_parse_label(src, cgl, mem); |
| if (!i->u.jc.l) return NULL; |
| } else { // j |
| i->tag = LIT_J; |
| if (!lil_parse_kw(src, "j")) return NULL; |
| i->u.j = lil_parse_label(src, cgl, mem); |
| if (!i->u.j) return NULL; |
| } |
| break; |
| case 'l': // =, locals, ld |
| if (num((*src)[1]) || (*src)[1]=='%') { |
| goto asgn; |
| } else if ((*src)[1]=='d') { |
| // ld |
| i->tag = LIT_Ld; |
| if (!lil_parse_kw(src, "ld")) return NULL; |
| if (!lil_parse_variable(src, va, &(i->u.ldst.operand.val.var))) return NULL; |
| if (!lil_parse_kw(src, ",")) return NULL; |
| if (!lil_parse_address(src, va, i)) return NULL; |
| if (!lil_parse_load_extend(src, i)) return NULL; |
| } else { |
| // locals |
| POINTER_SIZE_INT n; |
| i->tag = LIT_Locals; |
| if (!lil_parse_kw(src, "locals")) return NULL; |
| if (!lil_parse_number(src, va, &n)) return NULL; |
| i->u.locals = (unsigned) n; |
| } |
| break; |
| case 'm': // m2n_save_all |
| if (!lil_parse_kw(src, "m2n_save_all")) return NULL; |
| i->tag = LIT_M2NSaveAll; |
| break; |
| case 'o': // =, out |
| if (num((*src)[1]) || (*src)[1]=='%') goto asgn; |
| // out |
| i->tag = LIT_Out; |
| if (!lil_parse_kw(src, "out")) return NULL; |
| if (!lil_parse_sig(src, mem, va, &(i->u.out))) return NULL; |
| break; |
| case 'p': // push_m2n, pop_m2n, print |
| if ((*src)[1]=='u') { |
| // push_m2n |
| i->tag = LIT_PushM2N; |
| if (!lil_parse_kw(src, "push_m2n")) return NULL; |
| i->u.push_m2n.method = NULL; |
| i->u.push_m2n.current_frame_type = FRAME_UNKNOWN; |
| POINTER_SIZE_INT n; |
| POINTER_SIZE_INT ft; |
| if (!lil_parse_number(src, va, &n)) |
| return NULL; |
| else |
| i->u.push_m2n.method = (Method_Handle) n; |
| if (!lil_parse_kw(src, ",")) return NULL; |
| lil_skip_ws(src); |
| if (!lil_parse_number(src, va, &ft)) |
| return NULL; |
| else |
| i->u.push_m2n.current_frame_type = (frame_type)ft; |
| lil_skip_ws(src); |
| if ((*src)[0]!=';') { |
| if (!lil_parse_kw(src, ",")) return NULL; |
| if (!lil_parse_kw(src, "handles")) return NULL; |
| i->u.push_m2n.handles = true; |
| } else { |
| i->u.push_m2n.handles = false; |
| } |
| } |
| else if ((*src)[1]=='o') { |
| // pop_m2n |
| i->tag = LIT_PopM2N; |
| if (!lil_parse_kw(src, "pop_m2n")) return NULL; |
| } |
| else { |
| // print |
| i->tag = LIT_Print; |
| if (!lil_parse_kw(src, "print")) |
| return NULL; |
| lil_skip_ws(src); |
| if ((*src)[0] == '\'') { |
| // immediate string |
| if (!lil_parse_string(src, &i->u.print.str)) |
| return NULL; |
| } |
| else { |
| // look for string address |
| POINTER_SIZE_INT n; |
| if (!lil_parse_number(src, va, &n)) |
| return NULL; |
| else |
| i->u.print.str = (char *) n; |
| } |
| lil_skip_ws(src); |
| if ((*src)[0] == ';') { |
| // create a dummy immediate 0 operand |
| i->u.print.arg.is_immed = true; |
| i->u.print.arg.val.imm = 0; |
| i->u.print.arg.has_cast = false; |
| i->u.print.arg.t = LT_Ref; |
| } |
| else { |
| if (!lil_parse_kw(src, ",") || |
| !lil_parse_operand(src, va, &i->u.print.arg)) |
| return NULL; |
| } |
| } |
| break; |
| case 'r': // =, ret |
| if ((*src)[1]!='e') goto asgn; |
| // ret |
| i->tag = LIT_Ret; |
| if (!lil_parse_kw(src, "ret")) return NULL; |
| break; |
| case 's': // =, std_places, st |
| if ((*src)[1]=='p') goto asgn; |
| if ((*src)[1] && (*src)[2]=='d') { |
| // std_places |
| i->tag = LIT_StdPlaces; |
| if (!lil_parse_kw(src, "std_places")) return NULL; |
| POINTER_SIZE_INT n; |
| if (!lil_parse_number(src, va, &n)) |
| return NULL; |
| else |
| i->u.std_places = (unsigned) n; |
| } else { |
| // st |
| i->tag = LIT_St; |
| if (!lil_parse_kw(src, "st")) return NULL; |
| if (!lil_parse_address(src, va, i)) return NULL; |
| if (!lil_parse_kw(src, ",")) return NULL; |
| if (!lil_parse_operand(src, va, &(i->u.ldst.operand))) return NULL; |
| } |
| break; |
| case 't': // tailcall |
| i->tag = LIT_Call; |
| i->u.call.k = LCK_TailCall; |
| if (!lil_parse_kw(src, "tailcall")) return NULL; |
| if (!lil_parse_operand(src, va, &(i->u.call.target))) return NULL; |
| break; |
| default: |
| error(src, "expecting instruction", ""); |
| return NULL; |
| asgn: // =, v=ts |
| i->tag = LIT_Asgn; |
| if (!lil_parse_variable(src, va, &(i->u.asgn.dst))) return NULL; |
| if (!lil_parse_kw(src, "=")) return NULL; |
| lil_skip_ws(src); |
| // At this point there is either ts, a unary op, or an operand |
| if ((*src)[0]=='t') { |
| i->tag = LIT_Ts; |
| i->u.ts = i->u.asgn.dst; |
| if (!lil_parse_kw(src, "ts")) return NULL; |
| } else if ((*src)[0]=='n' || ((*src)[0]=='s' && (*src)[1]=='x') || (*src)[0]=='z') { |
| // Unary operation |
| // This code relies on parse_kw not changing src on failure since white space is skipped |
| if (lil_parse_kw_no_error(src, "not")) i->u.asgn.op = LO_Not; |
| else if (lil_parse_kw_no_error(src, "neg")) i->u.asgn.op = LO_Neg; |
| else if (lil_parse_kw_no_error(src, "sx1")) i->u.asgn.op = LO_Sx1; |
| else if (lil_parse_kw_no_error(src, "sx2")) i->u.asgn.op = LO_Sx2; |
| else if (lil_parse_kw_no_error(src, "sx4")) i->u.asgn.op = LO_Sx4; |
| else if (lil_parse_kw_no_error(src, "zx1")) i->u.asgn.op = LO_Zx1; |
| else if (lil_parse_kw_no_error(src, "zx2")) i->u.asgn.op = LO_Zx2; |
| else if (lil_parse_kw_no_error(src, "zx4")) i->u.asgn.op = LO_Zx4; |
| else { error(src, "expecting unary operation", ""); return NULL; } |
| if (!lil_parse_operand(src, va, &(i->u.asgn.o1))) return NULL; |
| } else { |
| // Operand |
| if (!lil_parse_operand(src, va, &(i->u.asgn.o1))) return NULL; |
| // Now the statement can either end or have a binary op followed by an operand |
| lil_skip_ws(src); |
| if ((*src)[0]!=';') { |
| if ((*src)[0]=='+') i->u.asgn.op = LO_Add, ++*src; |
| else if ((*src)[0]=='-') i->u.asgn.op = LO_Sub, ++*src; |
| else if ((*src)[0]=='*') i->u.asgn.op = LO_SgMul, ++*src; |
| else if ((*src)[0]=='<' && (*src)[1]=='<') i->u.asgn.op = LO_Shl, *src += 2; |
| else if ((*src)[0]=='&') i->u.asgn.op = LO_And, ++*src; |
| else { error(src, "expecting binary operation", ""); return NULL; } |
| if (!lil_parse_operand(src, va, &(i->u.asgn.o2))) return NULL; |
| } else { |
| i->u.asgn.op = LO_Mov; |
| } |
| } |
| break; |
| } |
| if (!lil_parse_kw(src,";")) return NULL; |
| return i; |
| } |
| |
| LilCodeStub* lil_parse_code_stub(const char* src, ...) |
| { |
| assert(src); |
| const char** cur = &src; |
| LilVarArgs va; |
| va.current = 0; |
| va_start(va.val, src); |
| |
| tl::MemoryPool* mem = new tl::MemoryPool; |
| LilCodeStub* cs = (LilCodeStub*)mem->alloc(sizeof(LilCodeStub)); |
| cs->my_memory = mem; |
| cs->cur_gen_label = 0; |
| cs->is = NULL; |
| cs->ctxt_state = LCSC_NotComputed; |
| cs->num_is = 0; |
| cs->init_ctxt = NULL; |
| cs->compiled_code_size = 0; |
| |
| LilInstruction *i, **tail=&(cs->is); |
| // originally no BB information is present; lil_cs_init_fg() will create it |
| cs->bb_list_head = NULL; |
| cs->num_bbs = 0; |
| |
| if (!lil_parse_kw(cur, "entry")) goto clean_up; |
| POINTER_SIZE_INT n; |
| if (!lil_parse_number(cur, &va, &n)) { |
| goto clean_up; |
| } |
| else { |
| cs->num_std_places = (unsigned) n; |
| } |
| if (!lil_parse_kw(cur, ":")) goto clean_up; |
| if (!lil_parse_sig(cur, mem, &va, &(cs->sig))) goto clean_up; |
| if (!lil_parse_kw(cur, ";")) goto clean_up; |
| |
| lil_skip_ws(cur); |
| while ((*cur)[0]) { |
| i = lil_parse_instruction(cur, mem, &cs->cur_gen_label, &va, &cs->sig); |
| if (!i) goto clean_up; |
| *tail = i; |
| i->next = NULL; |
| tail = &(i->next); |
| lil_skip_ws(cur); |
| cs->num_is++; |
| }; |
| |
| va_end(va.val); |
| return cs; |
| |
| clean_up: |
| va_end(va.val); |
| delete mem; |
| return NULL; |
| } |
| |
| LilCodeStub* lil_parse_onto_end(LilCodeStub* cs, const char* src, ...) |
| { |
| if (!cs) return NULL; |
| assert(src); |
| const char** cur = &src; |
| LilVarArgs va; |
| va.current = 0; |
| va_start(va.val, src); |
| |
| tl::MemoryPool* mem = cs->my_memory; |
| LilInstruction *i=cs->is, **tail=&(cs->is); |
| while (i) { tail = &i->next; i=i->next; } |
| |
| lil_skip_ws(cur); |
| while ((*cur)[0]) { |
| i = lil_parse_instruction(cur, mem, &cs->cur_gen_label, &va, &(cs->sig)); |
| if (!i) goto clean_up; |
| *tail = i; |
| i->next = NULL; |
| tail = &(i->next); |
| lil_skip_ws(cur); |
| cs->num_is++; |
| }; |
| |
| va_end(va.val); |
| return cs; |
| |
| clean_up: |
| va_end(va.val); |
| delete mem; |
| return NULL; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| // Contexts |
| |
| enum LilContextState { LCS_Unchanged, LCS_Changed, LCS_Error, LCS_Terminal }; |
| |
| struct LilInstructionContext { |
| LilContextState s; |
| unsigned num_std_places; |
| LilType* std_place_types; |
| LilM2nState m2n; |
| unsigned num_locals; |
| LilType* local_types; |
| LilSig* out_sig; |
| LilType ret; |
| unsigned amt_alloced; |
| }; |
| |
| static void lil_new_context2(LilCodeStub* cs, LilInstructionContext* ic) |
| { |
| ic->std_place_types = (LilType*)cs->my_memory->alloc(cs->max_std_places*sizeof(LilType)); |
| ic->local_types = (LilType*)cs->my_memory->alloc(cs->max_locals *sizeof(LilType)); |
| } |
| |
| static LilInstructionContext* lil_new_context(LilCodeStub* cs) |
| { |
| LilInstructionContext* ic = (LilInstructionContext*)cs->my_memory->alloc(sizeof(LilInstructionContext)); |
| lil_new_context2(cs, ic); |
| return ic; |
| } |
| |
| // Copy c1 to c2 |
| static void lil_copy_context(LilCodeStub* cs, LilInstructionContext* c1, LilInstructionContext* c2) |
| { |
| *c2 = *c1; |
| for(unsigned i=0; i<cs->max_std_places; i++) c2->std_place_types[i] = c1->std_place_types[i]; |
| for(unsigned j=0; j<cs->max_locals; j++) c2->local_types [j] = c1->local_types [j]; |
| } |
| |
| unsigned lil_ic_get_num_std_places(LilInstructionContext* ic) |
| { |
| return ic->num_std_places; |
| } |
| |
| LilM2nState lil_ic_get_m2n_state(LilInstructionContext* ic) |
| { |
| return ic->m2n; |
| } |
| |
| unsigned lil_ic_get_num_locals(LilInstructionContext* ic) |
| { |
| return ic->num_locals; |
| } |
| |
| LilType lil_ic_get_local_type(LilInstructionContext* ic, unsigned idx) |
| { |
| assert(idx<ic->num_locals); |
| return ic->local_types[idx]; |
| } |
| |
| LilSig* lil_ic_get_out_sig(LilInstructionContext* ic) |
| { |
| return ic->out_sig; |
| } |
| |
| LilType lil_ic_get_ret_type(LilInstructionContext* ic) |
| { |
| return ic->ret; |
| } |
| |
| unsigned lil_ic_get_amt_alloced(LilInstructionContext* ic) |
| { |
| return ic->amt_alloced; |
| } |
| |
| LilType lil_ic_get_type(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v, bool silent) |
| { |
| switch (v->tag) { |
| case LVK_In: |
| if (silent && v->index>=cs->sig.num_arg_types) return LT_Void; |
| assert(v->index<cs->sig.num_arg_types); |
| return cs->sig.arg_types[v->index]; |
| case LVK_StdPlace: |
| if (silent && v->index>=c->num_std_places) return LT_Void; |
| assert(v->index<c->num_std_places); |
| return c->std_place_types[v->index]; |
| case LVK_Local: |
| if (silent && v->index>=c->num_locals) return LT_Void; |
| assert(v->index<c->num_locals); |
| return c->local_types[v->index]; |
| //break; //remark #111: statement is unreachable |
| case LVK_Out: |
| if (silent && v->index>=c->out_sig->num_arg_types) return LT_Void; |
| assert(c->out_sig && v->index<c->out_sig->num_arg_types); |
| return c->out_sig->arg_types[v->index]; |
| case LVK_Ret: |
| return c->ret; |
| default: DIE(("Unknown variable kind")); for(;;); |
| } |
| } |
| |
| LilType lil_ic_get_type_aux(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o, bool silent) |
| { |
| if (o->has_cast) |
| return o->t; |
| else |
| if (o->is_immed) |
| return LT_PInt; |
| else |
| return lil_ic_get_type(cs, c, &o->val.var, silent); |
| } |
| |
| LilType lil_ic_get_type(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o) |
| { |
| return lil_ic_get_type_aux(cs, c, o, false); |
| } |
| |
| static void lil_ic_set_type(LilInstructionContext* c, LilVariable* v, LilType t) |
| { |
| switch (v->tag) { |
| case LVK_In: |
| // Ignore |
| break; |
| case LVK_StdPlace: |
| if (v->index<c->num_std_places) |
| c->std_place_types[v->index] = t; |
| break; |
| case LVK_Local: |
| if (v->index<c->num_locals) |
| c->local_types[v->index] = t; |
| break; |
| case LVK_Out: |
| // Ignore |
| break; |
| case LVK_Ret: |
| c->ret = t; |
| break; |
| default: DIE(("Unknown variable kind")); |
| } |
| } |
| |
| static LilType lil_type_asgn(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i); |
| |
| LilType lil_instruction_get_dest_type(LilCodeStub *cs, |
| LilInstruction *i, |
| LilInstructionContext *ctxt) { |
| switch (i->tag) { |
| case LIT_Alloc: |
| return LT_PInt; |
| case LIT_Asgn: |
| return lil_type_asgn(cs, ctxt, i); |
| case LIT_Ts: |
| return LT_PInt; |
| case LIT_Ld: |
| return i->u.ldst.t; |
| default: |
| return LT_Void; |
| } |
| } |
| |
| static LilType lil_type_unary_op(LilOperation op, LilType t) |
| { |
| switch (op) { |
| case LO_Mov: |
| case LO_Neg: |
| case LO_Not: |
| return t; |
| case LO_Sx1: |
| case LO_Sx2: |
| case LO_Sx4: |
| case LO_Zx1: |
| case LO_Zx2: |
| case LO_Zx4: |
| return t; |
| default: |
| DIE(("Unexpected operation")); |
| return LT_Void; |
| } |
| } |
| |
| static LilType lil_type_binary_op(LilOperation UNREF op, LilType t1, LilType UNREF t2) |
| { |
| return t1; |
| } |
| |
| static LilType lil_type_asgn(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i) |
| { |
| if (lil_operation_is_binary(i->u.asgn.op)) |
| return lil_type_binary_op(i->u.asgn.op, lil_ic_get_type_aux(cs, c, &i->u.asgn.o1, true), lil_ic_get_type_aux(cs, c, &i->u.asgn.o2, true)); |
| // ? 20040205: This is a hack to get the object allocation fastpath to type check |
| else if (i->u.asgn.op == LO_Sx4 && !i->u.asgn.o1.is_immed && i->u.asgn.o1.val.var.tag == LVK_In && i->u.asgn.o1.val.var.index == 0) |
| return LT_PInt; |
| else |
| return lil_type_unary_op(i->u.asgn.op, lil_ic_get_type_aux(cs, c, &i->u.asgn.o1, true)); |
| } |
| |
| // Compute the context at the beginning of an instruction given the context that falls through to it |
| // This function mutates c in place |
| static void lil_pre_context(LilInstructionContext* c, LilCodeStub* cs, LilInstruction* i) |
| { |
| if (i->tag==LIT_Label && i->u.label.ctxt) lil_copy_context(cs, i->u.label.ctxt, c); |
| } |
| |
| // Compute the context following the given instruction in the given code stub given the context before the instruction |
| // This function mutates c in place |
| // Note: |
| // 1) If the instruction is terminal the context state is set to LCS_Terminal |
| |
| static void lil_next_context(LilInstructionContext* c, LilCodeStub* cs, LilInstruction* i) |
| { |
| unsigned j; |
| switch (i->tag) { |
| case LIT_Label: |
| break; |
| case LIT_Locals: |
| c->num_locals = i->u.locals; |
| for(j=0; j<c->num_locals; j++) c->local_types[j] = LT_Void; |
| break; |
| case LIT_StdPlaces: |
| c->num_std_places = i->u.std_places; |
| for(j=0; j<c->num_std_places; j++) c->std_place_types[j] = LT_Void; |
| break; |
| case LIT_Alloc: |
| c->amt_alloced += i->u.alloc.num_bytes; |
| lil_ic_set_type(c, &i->u.alloc.dst, LT_PInt); |
| break; |
| case LIT_Asgn:{ |
| LilType t = lil_type_asgn(cs, c, i); |
| lil_ic_set_type(c, &i->u.asgn.dst, t); |
| break;} |
| case LIT_Ts: |
| lil_ic_set_type(c, &i->u.ts, LT_PInt); |
| break; |
| case LIT_Handles: |
| break; |
| case LIT_Ld: |
| lil_ic_set_type(c, &i->u.ldst.operand.val.var, (i->u.ldst.extend==LLX_None ? i->u.ldst.t : LT_PInt)); |
| break; |
| case LIT_St: |
| break; |
| case LIT_Inc: |
| break; |
| case LIT_Cas: |
| break; |
| case LIT_J: |
| c->s = LCS_Terminal; |
| break; |
| case LIT_Jc: |
| break; |
| case LIT_Out: |
| case LIT_In2Out: |
| c->out_sig = &i->u.out; |
| break; |
| case LIT_Call: |
| if (c->out_sig) c->ret = c->out_sig->ret_type; |
| c->num_std_places = 0; |
| c->out_sig = NULL; |
| if (i->u.call.k!=LCK_Call) c->s = LCS_Terminal; |
| break; |
| case LIT_Ret: |
| c->amt_alloced = 0; |
| c->num_locals = 0; |
| c->num_std_places = 0; |
| c->out_sig = NULL; |
| c->s = LCS_Terminal; |
| break; |
| case LIT_PushM2N: |
| c->m2n = (i->u.push_m2n.handles ? LMS_Handles : LMS_NoHandles); |
| c->out_sig = NULL; |
| c->ret = LT_Void; |
| break; |
| case LIT_M2NSaveAll: |
| break; |
| case LIT_PopM2N: |
| c->m2n = LMS_NoM2n; |
| c->amt_alloced = 0; |
| c->num_std_places = 0; |
| c->out_sig = NULL; |
| break; |
| case LIT_Print: |
| // nothing to do here |
| break; |
| default: DIE(("Unknown instruction tag")); |
| } |
| } |
| |
| // Are two signatures equal? |
| |
| static bool lil_sig_equal(LilSig* s1, LilSig* s2) |
| { |
| if (s1->cc!=s2->cc || s1->num_arg_types!=s2->num_arg_types || s1->ret_type!=s2->ret_type) return false; |
| for(unsigned i=0; i<s1->num_arg_types; i++) |
| if (s1->arg_types[i] != s2->arg_types[i]) return false; |
| return true; |
| } |
| |
| // Merge the contexts of two control flow edges |
| // This function mutates c1 in place to the merged context, setting the state to changed if it changed and error if the merge is invalid |
| |
| static void lil_merge_contexts(LilInstructionContext* c1, LilInstructionContext* c2) |
| { |
| if (c1->s==LCS_Error) return; |
| if (c1->num_std_places > c2->num_std_places) { c1->s = LCS_Changed; c1->num_std_places = c2->num_std_places; } |
| for(unsigned j=0; j<c1->num_std_places; j++) |
| if (c1->std_place_types[j]!=c2->std_place_types[j]) { c1->s = LCS_Changed; c1->num_std_places = j; break; } |
| if (c1->m2n != c2->m2n) { |
| fprintf(stderr, "Context mismatch: different M2N states\n"); |
| fflush(stderr); |
| c1->s = LCS_Error; |
| return; |
| } |
| if (c1->num_locals != c2->num_locals) { |
| fprintf(stderr, "Context mismatch: different number of locals\n"); |
| fflush(stderr); |
| c1->s = LCS_Error; |
| return; |
| } |
| for(unsigned k=0; k<c1->num_locals; k++) |
| if (c1->local_types[k]!=c2->local_types[k]) { |
| fprintf(stderr, "Context mismatch: different types at local %d\n", k); |
| fflush(stderr); |
| c1->s = LCS_Error; |
| return; |
| } |
| if (c1->out_sig) { |
| if (!c2->out_sig || !lil_sig_equal(c1->out_sig, c2->out_sig)) { |
| fprintf(stderr, "Context mismatch: different output signatures\n"); |
| fflush(stderr); |
| c1->s = LCS_Error; |
| return; |
| } |
| } else { |
| if (c2->out_sig) { |
| fprintf(stderr, "Context mismatch: different output signatures\n"); |
| fflush(stderr); |
| c1->s = LCS_Error; |
| return; } |
| } |
| if (c1->ret!=LT_Void && c1->ret!=c2->ret) { c1->s = LCS_Changed; c1->ret = LT_Void; } |
| if (c1->amt_alloced != c2->amt_alloced) { |
| fprintf(stderr, "Context mismatch: different allocated memory amounts\n"); |
| fflush(stderr); |
| c1->s = LCS_Error; |
| return; |
| } |
| } |
| |
| // Merge a context from a control transfer instruction into one of the label it transfers to |
| // returns false if the label is not found |
| |
| static bool lil_merge_context_with_label(LilCodeStub* cs, LilLabel l, LilInstructionContext* c) |
| { |
| LilInstruction* i = lil_find_label(cs, l); |
| if (i == NULL) |
| return false; |
| |
| if (i->u.label.ctxt) { |
| lil_merge_contexts(i->u.label.ctxt, c); |
| } else { |
| i->u.label.ctxt = lil_new_context(cs); |
| lil_copy_context(cs, c, i->u.label.ctxt); |
| i->u.label.ctxt->s = LCS_Changed; |
| } |
| return true; |
| } |
| |
| // Merge a fall through context into a label instruction |
| // Note that if c->s is LCS_Terminal then there was no fall through as the previous instruction was terminal |
| |
| static void lil_merge_context_with_label_instruction(LilCodeStub* cs, LilInstruction* i, LilInstructionContext* c) |
| { |
| if (c->s==LCS_Terminal) { |
| assert(i->u.label.ctxt); |
| lil_copy_context(cs, i->u.label.ctxt, c); |
| return; |
| } |
| if (i->u.label.ctxt) { |
| lil_merge_contexts(i->u.label.ctxt, c); |
| lil_copy_context(cs, i->u.label.ctxt, c); |
| } else { |
| i->u.label.ctxt = lil_new_context(cs); |
| lil_copy_context(cs, c, i->u.label.ctxt); |
| i->u.label.ctxt->s = LCS_Changed; |
| } |
| } |
| |
| // Compute the context of each label in a code stub so that contexts can be tracked as instructions are iterated over |
| |
| void lil_compute_contexts(LilCodeStub* cs) |
| { |
| // If already determined then return |
| if (!cs || cs->ctxt_state!=LCSC_NotComputed) return; |
| |
| // Count stuff |
| cs->max_std_places = cs->num_std_places; |
| cs->max_locals = 0; |
| for(LilInstruction* i = cs->is; i; i=i->next) { |
| if (i->tag==LIT_StdPlaces && i->u.std_places>cs->max_std_places) cs->max_std_places = i->u.std_places; |
| if (i->tag==LIT_Locals && i->u.locals>cs->max_locals) cs->max_locals = i->u.locals; |
| } |
| |
| // Compute the initial context |
| cs->init_ctxt = (LilInstructionContext*)cs->my_memory->alloc(sizeof(LilInstructionContext)); |
| lil_new_context2(cs, cs->init_ctxt); |
| cs->init_ctxt->s = LCS_Unchanged; |
| cs->init_ctxt->num_locals = 0; |
| cs->init_ctxt->out_sig = NULL; |
| cs->init_ctxt->num_std_places = cs->num_std_places; |
| for(unsigned k=0; k<cs->num_std_places; k++) cs->init_ctxt->std_place_types[k] = LT_PInt; |
| cs->init_ctxt->ret = LT_Void; |
| cs->init_ctxt->m2n = LMS_NoM2n; |
| cs->init_ctxt->amt_alloced = 0; |
| |
| // Fairly standard dataflow analysis |
| // while (something needs recomputing) |
| // iterate through instructions from beginning to end |
| // if label then: merge current context with label's stored context |
| // if j or jc or cas then: merge current context with target |
| // in all cases: compute next context |
| LilInstructionContext cur_ctxt; |
| lil_new_context2(cs, &cur_ctxt); |
| bool changed = true; |
| while (changed) { |
| changed = false; |
| lil_copy_context(cs, cs->init_ctxt, &cur_ctxt); |
| LilInstructionIterator iter(cs, false); |
| unsigned inum = 0; |
| while (!iter.at_end()) { |
| LilInstruction* i = iter.get_current(); |
| if (i->tag == LIT_Label) { |
| lil_merge_context_with_label_instruction(cs, i, &cur_ctxt); |
| if (!i->u.label.ctxt || i->u.label.ctxt->s==LCS_Error) { |
| fprintf(stderr, "Error merging contexts at label: %s\n", i->u.label.l); |
| fflush(stderr); |
| cs->ctxt_state = LCSC_Error; |
| return; |
| } |
| if (i->u.label.ctxt->s==LCS_Changed) { i->u.label.ctxt->s = LCS_Unchanged; changed = true; } |
| } |
| lil_next_context(&cur_ctxt, cs, i); |
| if (cur_ctxt.s==LCS_Error) { |
| fprintf(stderr, "Error: computed next context for instruction %d:\n", inum); |
| fflush(stderr); |
| lil_print_instruction(stderr, i); |
| cs->ctxt_state = LCSC_Error; |
| return; |
| } |
| if (i->tag == LIT_J) { |
| if (!lil_merge_context_with_label(cs, i->u.j, &cur_ctxt)) { |
| fprintf(stderr, "Error: label %s does not exist\n", i->u.j); |
| fflush(stderr); |
| cs->ctxt_state = LCSC_Error; |
| return; |
| } |
| } else if (i->tag == LIT_Jc) { |
| if (!lil_merge_context_with_label(cs, i->u.jc.l, &cur_ctxt)) { |
| fprintf(stderr, "Error: label %s does not exist\n", i->u.jc.l); |
| fflush(stderr); |
| cs->ctxt_state = LCSC_Error; |
| return; |
| } |
| } else if (i->tag == LIT_Cas) { |
| if (!lil_merge_context_with_label(cs, i->u.ldst.l, &cur_ctxt)) { |
| fprintf(stderr, "Error: label %s does not exist\n", i->u.ldst.l); |
| fflush(stderr); |
| cs->ctxt_state = LCSC_Error; |
| return; |
| } |
| } |
| iter.goto_next(); |
| ++inum; |
| } |
| } |
| cs->ctxt_state = LCSC_Computed; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| // Iteragators (other than validity) |
| |
| VMEXPORT LilSig* lil_cs_get_sig(LilCodeStub* cs) { return &cs->sig; } |
| |
| VMEXPORT unsigned lil_cs_get_num_instructions(LilCodeStub* cs) |
| { |
| return cs->num_is; |
| } |
| |
| VMEXPORT unsigned lil_cs_get_num_BBs(LilCodeStub *cs) { |
| if (cs->bb_list_head == NULL) |
| LilBb::init_fg(cs); |
| assert(cs->bb_list_head != NULL); |
| return cs->num_bbs; |
| } |
| |
| VMEXPORT LilBb* lil_cs_get_entry_BB(LilCodeStub* cs) { |
| if (cs->bb_list_head == NULL) |
| LilBb::init_fg(cs); |
| assert(cs->bb_list_head != NULL); |
| return cs->bb_list_head; |
| } |
| |
| VMEXPORT unsigned lil_cs_get_max_std_places(LilCodeStub * cs) { |
| return cs->max_std_places; |
| } |
| |
| VMEXPORT unsigned lil_cs_get_max_locals(LilCodeStub * cs) { |
| return cs->max_locals; |
| } |
| |
| VMEXPORT void lil_cs_set_code_size(LilCodeStub * cs, size_t size) { |
| cs->compiled_code_size = size; |
| } |
| |
| VMEXPORT size_t lil_cs_get_code_size(LilCodeStub * cs) { |
| return cs->compiled_code_size; |
| } |
| |
| static LilInstruction* lil_find_label(LilCodeStub* cs, LilLabel l) |
| { |
| for(LilInstruction* i=cs->is; i; i=i->next) |
| if (i->tag==LIT_Label && strcmp(i->u.label.l, l)==0) |
| return i; |
| return NULL; |
| } |
| |
| VMEXPORT LilInstructionIterator::LilInstructionIterator(LilCodeStub* _cs, bool _track_ctxt) |
| : cs(_cs), bb(NULL), cur(_cs->is), track_ctxt(_track_ctxt) |
| { |
| if (track_ctxt) { |
| lil_compute_contexts(cs); |
| ctxt = lil_new_context(cs); |
| lil_copy_context(cs, cs->init_ctxt, ctxt); |
| if (cs->is) lil_pre_context(ctxt, cs, cs->is); |
| } |
| } |
| |
| |
| VMEXPORT LilInstructionIterator::LilInstructionIterator(LilCodeStub* _cs, LilBb *_bb, bool _track_ctxt) |
| : cs(_cs), bb(_bb), track_ctxt(_track_ctxt) |
| { |
| assert(bb != NULL); |
| cur = bb->get_first(); |
| |
| if (track_ctxt) { |
| lil_compute_contexts(cs); |
| ctxt = lil_new_context(cs); |
| // current context is the initial context of the BB! |
| lil_copy_context(cs, _bb->get_context(), ctxt); |
| if (cur) |
| lil_pre_context(ctxt, cs, cur); |
| } |
| } |
| |
| |
| VMEXPORT bool LilInstructionIterator::at_end() |
| { |
| return cur==NULL; |
| } |
| |
| VMEXPORT LilInstruction* LilInstructionIterator::get_current() |
| { |
| return cur; |
| } |
| |
| VMEXPORT LilInstructionContext* LilInstructionIterator::get_context() |
| { |
| assert(track_ctxt); |
| return ctxt; |
| } |
| |
| VMEXPORT void LilInstructionIterator::goto_next() |
| { |
| if (track_ctxt && cur) lil_next_context(ctxt, cs, cur); |
| if (cur) { |
| // if this is a BB iterator, gotta check for BB end |
| if (bb != NULL && cur == bb->get_last()) |
| cur = NULL; |
| else |
| cur = cur->next; |
| } |
| if (track_ctxt && cur) lil_pre_context(ctxt, cs, cur); |
| } |
| |
| |
| VMEXPORT LilInstructionVisitor::LilInstructionVisitor() |
| { |
| } |
| |
| |
| void lil_visit_instruction(LilInstruction* i, LilInstructionVisitor* v) |
| { |
| switch (i->tag) { |
| case LIT_Label: |
| v->label(i->u.label.l); |
| break; |
| case LIT_Locals: |
| v->locals(i->u.locals); |
| break; |
| case LIT_StdPlaces: |
| v->std_places(i->u.std_places); |
| break; |
| case LIT_Alloc: |
| v->alloc(&i->u.alloc.dst, i->u.alloc.num_bytes); |
| break; |
| case LIT_Asgn: |
| v->asgn(&i->u.asgn.dst, i->u.asgn.op, &i->u.asgn.o1, &i->u.asgn.o2); |
| break; |
| case LIT_Ts: |
| v->ts(&i->u.ts); |
| break; |
| case LIT_Handles: |
| v->handles(&i->u.handles); |
| break; |
| case LIT_Ld: |
| v->ld(i->u.ldst.t, &i->u.ldst.operand.val.var, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), |
| i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, i->u.ldst.extend); |
| break; |
| case LIT_St: |
| v->st(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), |
| i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, |
| &i->u.ldst.operand); |
| break; |
| case LIT_Inc: |
| v->inc(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), |
| i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel); |
| break; |
| case LIT_Cas: |
| v->cas(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), |
| i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, |
| &i->u.ldst.compare, &i->u.ldst.operand, i->u.ldst.l); |
| break; |
| case LIT_J: |
| v->j(i->u.j); |
| break; |
| case LIT_Jc: |
| v->jc(i->u.jc.c.tag, &i->u.jc.c.o1, &i->u.jc.c.o2, i->u.jc.l); |
| break; |
| case LIT_Out: |
| v->out(&i->u.out); |
| break; |
| case LIT_In2Out: |
| v->in2out(&i->u.out); |
| break; |
| case LIT_Call: |
| v->call(&i->u.call.target, i->u.call.k); |
| break; |
| case LIT_Ret: |
| v->ret(); |
| break; |
| case LIT_PushM2N: |
| v->push_m2n(i->u.push_m2n.method, i->u.push_m2n.current_frame_type, i->u.push_m2n.handles); |
| break; |
| case LIT_M2NSaveAll: |
| v->m2n_save_all(); |
| break; |
| case LIT_PopM2N: |
| v->pop_m2n(); |
| break; |
| case LIT_Print: |
| v->print(i->u.print.str, &i->u.print.arg); |
| break; |
| default: DIE(("Unknown instruction tag")); |
| } |
| } |
| |
| LilVariableKind lil_variable_get_kind(LilVariable* v) { return v->tag; } |
| unsigned lil_variable_get_index(LilVariable* v) { return v->index; } |
| |
| bool lil_variable_is_equal(LilVariable* v1, LilVariable* v2) |
| { |
| return v1->tag==v2->tag && v1->index==v2->index; |
| } |
| |
| bool lil_operand_is_immed(LilOperand* o) { return o->is_immed; } |
| POINTER_SIZE_INT lil_operand_get_immed(LilOperand* o) { |
| assert(o->is_immed); |
| return o->val.imm; |
| } |
| |
| LilVariable* lil_operand_get_variable(LilOperand* o) { |
| assert(!o->is_immed); |
| return &o->val.var; |
| } |
| |
| LilCc lil_sig_get_cc(LilSig* sig) { return sig->cc; } |
| bool lil_sig_is_arbitrary(LilSig* sig) { return sig->arbitrary; } |
| unsigned lil_sig_get_num_args(LilSig* sig) { return sig->num_arg_types; } |
| LilType lil_sig_get_arg_type(LilSig* sig, unsigned num) { |
| assert(num < sig->num_arg_types); |
| return sig->arg_types[num]; |
| } |
| LilType lil_sig_get_ret_type(LilSig* sig) { return sig->ret_type; } |
| |
| bool lil_predicate_is_binary(enum LilPredicate c) |
| { |
| switch (c) { |
| case LP_IsZero: |
| case LP_IsNonzero: |
| return false; |
| case LP_Eq: |
| case LP_Ne: |
| case LP_Le: |
| case LP_Lt: |
| case LP_Ule: |
| case LP_Ult: |
| return true; |
| default: |
| DIE(("Unknown predicate")); |
| return false; // not reached |
| } |
| } |
| |
| bool lil_predicate_is_signed(LilPredicate p) { |
| return (p == LP_Ule || p == LP_Ult); |
| } |
| |
| bool lil_operation_is_binary(enum LilOperation op) |
| { |
| switch (op) { |
| case LO_Mov: |
| case LO_Neg: |
| case LO_Not: |
| case LO_Sx1: |
| case LO_Sx2: |
| case LO_Sx4: |
| case LO_Zx1: |
| case LO_Zx2: |
| case LO_Zx4: |
| return false; |
| case LO_Add: |
| case LO_Sub: |
| case LO_SgMul: |
| case LO_Shl: |
| case LO_And: |
| return true; |
| default: |
| DIE(("Unknown operation")); |
| return false; // not reached |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| // Validity |
| |
| static bool lil_is_valid_asgn(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v, LilType t) |
| { |
| switch (v->tag) { |
| case LVK_In: |
| return v->index<cs->sig.num_arg_types && cs->sig.arg_types[v->index]==t; |
| case LVK_StdPlace: |
| return v->index<c->num_std_places; |
| case LVK_Local: |
| return v->index<c->num_locals; |
| case LVK_Out: |
| return c->out_sig && v->index<c->out_sig->num_arg_types && c->out_sig->arg_types[v->index]==t; |
| case LVK_Ret: |
| return v->index<1; |
| default: |
| return false; |
| } |
| } |
| |
| static bool lil_is_valid_variable(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v) |
| { |
| switch (v->tag) { |
| case LVK_In: return v->index<cs->sig.num_arg_types; |
| case LVK_StdPlace: return v->index<c->num_std_places; |
| case LVK_Local: return v->index<c->num_locals; |
| case LVK_Out: return c->out_sig && v->index<c->out_sig->num_arg_types; |
| case LVK_Ret: return v->index<1 && c->ret!=LT_Void; |
| default: return false; |
| } |
| } |
| |
| #ifdef _IPF_ |
| #define PLATFORM_INT LT_G8 |
| #else |
| #define PLATFORM_INT LT_G4 |
| #endif |
| |
| static bool lil_are_types_compatible(LilType t1, LilType t2) |
| { |
| return t1==t2 || ((t1==LT_Ref || t1==LT_PInt || t1==PLATFORM_INT) && (t2==LT_Ref || t2==LT_PInt || t2==PLATFORM_INT)); |
| } |
| |
| static bool lil_is_valid_operand(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o) |
| { |
| if (!o->is_immed && !lil_is_valid_variable(cs, c, &(o->val.var))) return false; |
| if (o->has_cast) { |
| LilType raw_type = lil_ic_get_type(cs, c, o); |
| if (!lil_are_types_compatible(o->t, raw_type)) return false; |
| } |
| return true; |
| } |
| |
| static bool lil_is_valid_address(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i) |
| { |
| if (i->u.ldst.is_base) { |
| if (!lil_is_valid_variable(cs, c, &(i->u.ldst.base))) return false; |
| LilType t = lil_ic_get_type(cs, c, &(i->u.ldst.base), true); |
| if (t!=LT_Ref && t!=LT_PInt && t!=PLATFORM_INT) return false; |
| } |
| if (i->u.ldst.is_index) { |
| if (!lil_is_valid_variable(cs, c, &(i->u.ldst.index))) return false; |
| LilType t = lil_ic_get_type(cs, c, &(i->u.ldst.index), true); |
| if (t!=LT_Ref && t!=LT_PInt && t!=PLATFORM_INT) return false; |
| } |
| return true; |
| } |
| |
| static bool lil_is_valid_label(LilCodeStub* cs, LilLabel l) |
| { |
| return lil_find_label(cs, l)!=NULL; |
| } |
| |
| static bool lil_verify_unary_op(LilOperation UNREF op, LilType UNREF t) |
| { |
| return true; |
| } |
| |
| static bool lil_verify_binary_op(LilOperation op, LilType t1, LilType t2) |
| { |
| if (op==LO_Shl) |
| return t2==LT_PInt; |
| else |
| return t1==t2; |
| } |
| |
| static bool lil_verify_binary_cond(LilPredicate UNREF p, LilType t1, LilType t2) |
| { |
| return t1==t2; |
| } |
| |
| static bool lil_print_err(const char *s, LilInstruction* i, unsigned inst_number) |
| { |
| fprintf(stderr, "lil code stub invalid at instruction %d: %s\n ", inst_number, s); |
| if (i) lil_print_instruction(stdout, i); |
| fflush(stderr); |
| return false; |
| } |
| |
| #define ERR(s) { return lil_print_err(s, i, inst_number); } |
| |
| bool lil_is_valid(LilCodeStub* cs) |
| { |
| unsigned inst_number = 0; |
| LilInstruction* i = NULL; |
| |
| if (!cs) ERR("code stub is null"); |
| |
| lil_compute_contexts(cs); |
| if (cs->ctxt_state == LCSC_Error) ERR("control flow contexts inconsistent"); |
| |
| // Check instructions |
| bool last_was_terminal = false; |
| LilInstructionIterator iter(cs, true); |
| while (!iter.at_end()) { |
| inst_number++; |
| i = iter.get_current(); |
| LilInstructionContext* c = iter.get_context(); |
| |
| switch (i->tag) { |
| case LIT_Label: |
| { |
| LilLabel l = i->u.label.l; |
| LilInstruction *label_def = lil_find_label(cs, l); |
| if (label_def != i) { |
| char buffer[256]; |
| sprintf(buffer, "label %s redefined", l); |
| ERR(buffer); |
| } |
| break; |
| } |
| case LIT_Locals: |
| break; |
| case LIT_StdPlaces: |
| break; |
| case LIT_Alloc: |
| if (c->out_sig) ERR("alloc between out and call"); |
| if (!lil_is_valid_variable(cs, c, &(i->u.alloc.dst))) ERR("invalid variable in alloc"); |
| break; |
| case LIT_Asgn: |
| if (!lil_is_valid_operand(cs, c, &(i->u.asgn.o1))) ERR("invalid first operand in assignment"); |
| if (lil_operation_is_binary(i->u.asgn.op)) { |
| if (!lil_is_valid_operand(cs, c, &(i->u.asgn.o2))) ERR("invalid second operand in assignment"); |
| if (!lil_verify_binary_op(i->u.asgn.op, lil_ic_get_type(cs, c, &i->u.asgn.o1), lil_ic_get_type(cs, c, &i->u.asgn.o2))) |
| ERR("operand type mismatch in assignment"); |
| } else { |
| if (!lil_verify_unary_op(i->u.asgn.op, lil_ic_get_type(cs, c, &i->u.asgn.o1))) |
| ERR("operand type mismatch in assignment"); |
| } |
| // ? 20040205: This is a hack to get the object allocation fastpath to type check |
| if (i->u.asgn.op == LO_Sx4 && !i->u.asgn.o1.is_immed && i->u.asgn.o1.val.var.tag == LVK_In && i->u.asgn.o1.val.var.index == 0) { |
| if (!lil_is_valid_asgn(cs, c, &i->u.asgn.dst, LT_PInt)) |
| ERR("invalid destination or type incompatibility in assignment"); |
| } else if (!lil_is_valid_asgn(cs, c, &i->u.asgn.dst, lil_type_asgn(cs, c, i))) { |
| ERR("invalid destination or type incompatibility in assignment"); |
| } |
| break; |
| case LIT_Ts: |
| if (!lil_is_valid_asgn(cs, c, &i->u.ts, LT_PInt)) ERR("invalid destination in ts"); |
| break; |
| case LIT_Handles: |
| if (c->m2n!=LMS_Handles) ERR("handles not dominated by push_m2n handles"); |
| if (!lil_is_valid_operand(cs, c, &(i->u.handles))) ERR("invalid operand in handles"); |
| if (lil_ic_get_type(cs, c, &i->u.handles)!=LT_PInt) ERR("operand not platform int in handles"); |
| break; |
| case LIT_Ld: |
| if (!lil_is_valid_asgn(cs, c, &i->u.ldst.operand.val.var, (i->u.ldst.extend==LLX_None ? i->u.ldst.t : LT_PInt))) |
| ERR("invalid destination in load"); |
| if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in load"); |
| break; |
| case LIT_St: |
| if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in store"); |
| if (!i->u.ldst.operand.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in store"); |
| if (!lil_is_valid_operand(cs, c, &(i->u.ldst.operand))) ERR("invalid source in store"); |
| break; |
| case LIT_Inc: |
| if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in inc"); |
| break; |
| case LIT_Cas: |
| if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in cas"); |
| if (!i->u.ldst.compare.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in cas compare"); |
| if (!i->u.ldst.operand.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in cas source"); |
| if (!lil_is_valid_operand(cs, c, &(i->u.ldst.compare))) ERR("invalid compare in cas"); |
| if (!lil_is_valid_operand(cs, c, &(i->u.ldst.operand))) ERR("invalid source in cas"); |
| if (!lil_is_valid_label(cs, i->u.ldst.l)) ERR("bad target in cas"); |
| break; |
| case LIT_J: |
| if (!lil_is_valid_label(cs, i->u.j)) ERR("bad target in j"); |
| break; |
| case LIT_Jc: |
| // Should do typechecks here |
| if (!lil_is_valid_label(cs, i->u.jc.l)) ERR("bad target in jc"); |
| if (!lil_is_valid_operand(cs, c, &(i->u.jc.c.o1))) ERR("invalid first operand in condition"); |
| if (lil_predicate_is_binary(i->u.jc.c.tag)) { |
| if (!lil_is_valid_operand(cs, c, &(i->u.jc.c.o2))) ERR("invalid second operand in condition"); |
| if (!lil_verify_binary_cond(i->u.jc.c.tag, lil_ic_get_type(cs, c, &(i->u.jc.c.o1)), lil_ic_get_type(cs, c, &(i->u.jc.c.o2)))) |
| ERR("operand type mismatch in conditional jump"); |
| } |
| break; |
| case LIT_Out: |
| break; |
| case LIT_In2Out: |
| if (cs->sig.arbitrary) ERR("in2out in arbitrary code stub"); |
| break; |
| case LIT_Call: |
| if (i->u.call.k!=LCK_TailCall && !c->out_sig) ERR("call not dominated by out or in2out"); |
| if (!lil_is_valid_operand(cs, c, &(i->u.call.target))) ERR("invalid operand in call"); |
| if (lil_ic_get_type(cs, c, &i->u.call.target)!=LT_PInt) ERR("operand not platform int in call"); |
| break; |
| case LIT_Ret: |
| if (cs->sig.arbitrary) ERR("cannot return from an arbitrary signature entry"); |
| if (cs->sig.ret_type!=LT_Void && cs->sig.ret_type!=c->ret) |
| ERR("ret with invalid return value"); |
| if (c->m2n) ERR("return with m2n"); |
| break; |
| case LIT_PushM2N: |
| if (c->amt_alloced>0) ERR("alloc before push_m2n"); |
| if (c->m2n!=LMS_NoM2n) ERR("push m2n twice"); |
| break; |
| case LIT_M2NSaveAll: |
| if (c->m2n==LMS_NoM2n) ERR("m2n save all not dominated by push m2n"); |
| break; |
| case LIT_PopM2N: |
| if (c->m2n==LMS_NoM2n) ERR("pop m2n not dominated by push"); |
| break; |
| case LIT_Print: |
| if (!lil_is_valid_operand(cs, c, &i->u.print.arg)) |
| ERR("invalid argument to print"); |
| break; |
| default: |
| ERR("unknown instruction"); |
| } |
| |
| iter.goto_next(); |
| last_was_terminal = (c->s==LCS_Terminal); |
| } |
| |
| if (!last_was_terminal) ERR("last instruction not terminal"); |
| |
| return true; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| // Printing Utilities |
| |
| void lil_print_cc(FILE* out, enum LilCc cc) |
| { |
| switch (cc) { |
| case LCC_Platform: fprintf(out, "platform"); break; |
| case LCC_Managed: fprintf(out, "managed"); break; |
| case LCC_Rth: fprintf(out, "rth"); break; |
| case LCC_Jni: fprintf(out, "jni"); break; |
| case LCC_StdCall: fprintf(out, "stdcall"); break; |
| default: DIE(("Unknown calling convention")); |
| } |
| fflush(out); |
| } |
| |
| void lil_print_type(FILE* out, enum LilType t) |
| { |
| switch (t) { |
| case LT_G1: fprintf(out, "g1"); break; |
| case LT_G2: fprintf(out, "g2"); break; |
| case LT_G4: fprintf(out, "g4"); break; |
| case LT_G8: fprintf(out, "g8"); break; |
| case LT_F4: fprintf(out, "f4"); break; |
| case LT_F8: fprintf(out, "f8"); break; |
| case LT_Ref: fprintf(out, "ref"); break; |
| case LT_PInt: fprintf(out, "pint"); break; |
| case LT_Void: fprintf(out, "void"); break; |
| default: DIE(("Unknown LIL type")); |
| } |
| fflush(out); |
| } |
| |
| void lil_print_sig(FILE* out, LilSig* sig) |
| { |
| assert(sig); |
| lil_print_cc(out, sig->cc); |
| fprintf(out, ":"); |
| if (sig->arbitrary) { |
| fprintf(out, "arbitrary"); |
| } else { |
| for(unsigned i=0; i<sig->num_arg_types; i++) { |
| if (i>0) fprintf(out, ","); |
| lil_print_type(out, sig->arg_types[i]); |
| } |
| fprintf(out, ":"); |
| lil_print_type(out, sig->ret_type); |
| } |
| fflush(out); |
| } |
| |
| void lil_print_variable(FILE* out, LilVariable* v) |
| { |
| assert(v); |
| switch (v->tag) { |
| case LVK_In: |
| fprintf(out, "i"); |
| break; |
| case LVK_StdPlace: |
| fprintf(out, "sp"); |
| break; |
| case LVK_Out: |
| fprintf(out, "o"); |
| break; |
| case LVK_Local: |
| fprintf(out, "l"); |
| break; |
| case LVK_Ret: |
| assert(v->index==0); |
| fprintf(out, "r"); |
| return; |
| default: DIE(("Unknown kind")); |
| } |
| fprintf(out, "%d", v->index); |
| fflush(out); |
| } |
| |
| void lil_print_operand(FILE* out, LilOperand* o) |
| { |
| assert(o); |
| if (o->is_immed) |
| // since imm is POINTER_SIZE_INT, %p should work in all cases |
| fprintf(out, "0x%p", (void *)o->val.imm); |
| else |
| lil_print_variable(out, &(o->val.var)); |
| if (o->has_cast) { |
| fprintf(out, ":"); |
| lil_print_type(out, o->t); |
| } |
| fflush(out); |
| } |
| |
| void lil_print_address(FILE* out, LilInstruction* i) |
| { |
| bool printed_term = false; |
| fprintf(out, "["); |
| if (i->u.ldst.is_base) { |
| lil_print_variable(out, &(i->u.ldst.base)); |
| printed_term = true; |
| } |
| if (i->u.ldst.is_index) { |
| if (printed_term) |
| fprintf(out, "+"); |
| fprintf(out, "%d*", i->u.ldst.scale); |
| lil_print_variable(out, &(i->u.ldst.index)); |
| printed_term = true; |
| } |
| if (i->u.ldst.offset != 0) { |
| if (printed_term) |
| fprintf(out, "+"); |
| fprintf(out, "0x%p", (void *)i->u.ldst.offset); |
| printed_term = true; |
| } |
| if (!printed_term) |
| fprintf(out, "0x0"); |
| fprintf(out, ":"); |
| lil_print_type(out, i->u.ldst.t); |
| switch (i->u.ldst.acqrel) { |
| case LAR_None: break; |
| case LAR_Acquire: fprintf(out, ",acquire"); break; |
| case LAR_Release: fprintf(out, ",release"); break; |
| default: DIE(("Unexpected acqrel value")); |
| } |
| fprintf(out, "]"); |
| fflush(out); |
| } |
| |
| void lil_print_instruction(FILE* out, LilInstruction* i) |
| { |
| assert(i); |
| switch (i->tag) { |
| case LIT_Label: |
| fprintf(out, ":%s", i->u.label.l); |
| break; |
| case LIT_Locals: |
| fprintf(out, "locals %d", i->u.locals); |
| break; |
| case LIT_StdPlaces: |
| fprintf(out, "std_places %d", i->u.std_places); |
| break; |
| case LIT_Alloc: |
| fprintf(out, "alloc "); |
| lil_print_variable(out, &i->u.alloc.dst); |
| fprintf(out, ",0x%x", i->u.alloc.num_bytes); |
| break; |
| case LIT_Asgn: |
| lil_print_variable(out, &(i->u.asgn.dst)); |
| fprintf(out, "="); |
| switch (i->u.asgn.op) { |
| case LO_Mov: |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Add: |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| fprintf(out, "+"); |
| lil_print_operand(out, &(i->u.asgn.o2)); |
| break; |
| case LO_Sub: |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| fprintf(out, "-"); |
| lil_print_operand(out, &(i->u.asgn.o2)); |
| break; |
| case LO_SgMul: |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| fprintf(out, "*"); |
| lil_print_operand(out, &(i->u.asgn.o2)); |
| break; |
| case LO_Shl: |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| fprintf(out, "<<"); |
| lil_print_operand(out, &(i->u.asgn.o2)); |
| break; |
| case LO_And: |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| fprintf(out, "&"); |
| lil_print_operand(out, &(i->u.asgn.o2)); |
| break; |
| case LO_Neg: |
| fprintf(out, "-"); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Not: |
| fprintf(out, "not "); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Sx1: |
| fprintf(out, "sx1 "); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Sx2: |
| fprintf(out, "sx2 "); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Sx4: |
| fprintf(out, "sx4 "); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Zx1: |
| fprintf(out, "zx1 "); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Zx2: |
| fprintf(out, "zx2 "); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| case LO_Zx4: |
| fprintf(out, "zx4 "); |
| lil_print_operand(out, &(i->u.asgn.o1)); |
| break; |
| default: DIE(("Unknown operation")); |
| } |
| break; |
| case LIT_Ts: |
| lil_print_variable(out, &i->u.ts); |
| fprintf(out, "=ts"); |
| break; |
| case LIT_Handles: |
| fprintf(out, "handles="); |
| lil_print_operand(out, &i->u.handles); |
| break; |
| case LIT_Ld: |
| fprintf(out, "ld "); |
| lil_print_variable(out, &(i->u.ldst.operand.val.var)); |
| fprintf(out, ","); |
| lil_print_address(out, i); |
| if (i->u.ldst.extend==LLX_Sign) fprintf(out, ",sx"); |
| if (i->u.ldst.extend==LLX_Zero) fprintf(out, ",zx"); |
| break; |
| case LIT_St: |
| fprintf(out, "st "); |
| lil_print_address(out, i); |
| fprintf(out, ","); |
| lil_print_operand(out, &(i->u.ldst.operand)); |
| break; |
| case LIT_Inc: |
| fprintf(out, "inc "); |
| lil_print_address(out, i); |
| break; |
| case LIT_Cas: |
| fprintf(out, "cas "); |
| lil_print_address(out, i); |
| fprintf(out, "="); |
| lil_print_operand(out, &(i->u.ldst.compare)); |
| fprintf(out, ","); |
| lil_print_operand(out, &(i->u.ldst.operand)); |
| fprintf(out, ",%s", i->u.ldst.l); |
| break; |
| case LIT_J: |
| fprintf(out, "j %s", i->u.j); |
| break; |
| case LIT_Jc: |
| fprintf(out, "jc "); |
| switch (i->u.jc.c.tag) { |
| case LP_IsZero: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, "=0"); |
| break; |
| case LP_IsNonzero: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, "!=0"); |
| break; |
| case LP_Eq: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, "="); |
| lil_print_operand(out, &(i->u.jc.c.o2)); |
| break; |
| case LP_Ne: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, "!="); |
| lil_print_operand(out, &(i->u.jc.c.o2)); |
| break; |
| case LP_Le: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, "<="); |
| lil_print_operand(out, &(i->u.jc.c.o2)); |
| break; |
| case LP_Lt: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, "<"); |
| lil_print_operand(out, &(i->u.jc.c.o2)); |
| break; |
| case LP_Ule: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, " <=u "); |
| lil_print_operand(out, &(i->u.jc.c.o2)); |
| break; |
| case LP_Ult: |
| lil_print_operand(out, &(i->u.jc.c.o1)); |
| fprintf(out, " <u "); |
| lil_print_operand(out, &(i->u.jc.c.o2)); |
| break; |
| default: DIE(("Unknown predicate")); |
| } |
| fprintf(out, ",%s", i->u.jc.l); |
| break; |
| case LIT_Out: |
| fprintf(out, "out "); |
| lil_print_sig(out, &(i->u.out)); |
| break; |
| case LIT_In2Out: |
| fprintf(out, "in2out "); |
| lil_print_cc(out, i->u.out.cc); |
| fprintf(out, ":"); |
| lil_print_type(out, i->u.out.ret_type); |
| break; |
| case LIT_Call: |
| switch (i->u.call.k) { |
| case LCK_Call: |
| fprintf(out, "call "); |
| break; |
| case LCK_CallNoRet: |
| fprintf(out, "call.noret "); |
| break; |
| case LCK_TailCall: |
| fprintf(out, "tailcall "); |
| break; |
| default: DIE(("Unknown call kind")); |
| } |
| lil_print_operand(out, &(i->u.call.target)); |
| break; |
| case LIT_Ret: |
| fprintf(out, "ret"); |
| break; |
| case LIT_PushM2N: |
| fprintf(out, "push_m2n %p, frame_type= %p", i->u.push_m2n.method, i->u.push_m2n.current_frame_type); |
| if (i->u.push_m2n.handles) fprintf(out, ",handles"); |
| break; |
| case LIT_M2NSaveAll: |
| fprintf(out, "m2n_save_all"); |
| break; |
| case LIT_PopM2N: |
| fprintf(out, "pop_m2n"); |
| break; |
| case LIT_Print: |
| fprintf(out, "print %p, ", i->u.print.str); |
| lil_print_operand(out, &i->u.print.arg); |
| fprintf(out, "\n"); |
| break; |
| default: |
| DIE(("Unknown instruction tag")); |
| }; |
| fprintf(out, ";\n"); |
| fflush(out); |
| } |
| |
| void lil_print_entry(FILE* out, LilCodeStub* cs) |
| { |
| fprintf(out, "entry %d:", cs->num_std_places); |
| lil_print_sig(out, &(cs->sig)); |
| fprintf(out, ";\n"); |
| fflush(out); |
| } |
| |
| void lil_print_code_stub(FILE* out, LilCodeStub* cs) |
| { |
| assert(cs); |
| lil_print_entry(out, cs); |
| for(LilInstruction* i=cs->is; i; i=i->next) |
| lil_print_instruction(out, i); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| // Basic Blocks |
| |
| // private constructor; create BBs by calling init_fg() |
| LilBb::LilBb(LilCodeStub *_cs, LilInstruction *_start, |
| LilInstructionContext* ctxt_at_start): |
| cs(_cs), start(_start), end(NULL), |
| ctxt(NULL), |
| fallthru(NULL), |
| branch_target(NULL), |
| num_pred(0), |
| next(NULL), id(-1) |
| { |
| // add this to the end of the stub's BB list |
| if (cs->bb_list_head == NULL) |
| cs->bb_list_head = this; |
| else { |
| LilBb* last_bb = cs->bb_list_head; |
| while (last_bb->next != NULL) |
| last_bb = last_bb->next; |
| last_bb->next = this; |
| } |
| |
| // store a copy of the current context in ctxt |
| assert(ctxt_at_start != NULL); |
| ctxt = lil_new_context(cs); |
| lil_copy_context(cs, ctxt_at_start, ctxt); |
| } // LilBb::LilBb |
| |
| // private operator new; create BBs using new_bb() only |
| void* LilBb::operator new(size_t sz, tl::MemoryPool& m) { |
| return m.alloc(sz); |
| } // LilBb::operator new |
| |
| int LilBb::get_id() { |
| return id; |
| } // LilBb::get_id |
| |
| // sets the last instruction of the BB |
| void LilBb::set_last(LilInstruction *i) { |
| // can't set the last instruction twice! |
| assert(end == NULL); |
| end = i; |
| } // LilBb::set_last |
| |
| // gets the first instruction |
| LilInstruction* LilBb::get_first() { |
| return start; |
| } // LilBb::get_first |
| |
| // gets the last instruction |
| LilInstruction* LilBb::get_last() { |
| return end; |
| } // LilBb::get_last |
| |
| LilInstructionContext* LilBb::get_context() { |
| return ctxt; |
| } // LilBb::get_context |
| |
| // does this bb contain instruction i? |
| bool LilBb::contains(LilInstruction *i) { |
| for (LilInstruction* j = start; j != NULL; j++) { |
| if (j == i) |
| return true; |
| if (j == end) |
| break; |
| } |
| return false; |
| } // LilBb::contains |
| |
| // get the label of this BB; NULL if no label exists |
| LilLabel LilBb::get_label() { |
| if (start->tag == LIT_Label) |
| return start->u.label.l; |
| return NULL; |
| } // LilBb::get_label |
| |
| // set a fallthrough successor to this bb |
| void LilBb::set_fallthru(LilBb *succ) { |
| fallthru = succ; |
| assert(succ->num_pred < MAX_BB_PRED); |
| succ->pred[succ->num_pred++] = this; |
| } // LilBb::set_fallthru |
| |
| // set a branch-target successor to this bb |
| void LilBb::set_branch_target(LilBb *succ) { |
| branch_target = succ; |
| assert(succ->num_pred < MAX_BB_PRED); |
| succ->pred[succ->num_pred++] = this; |
| } // LilBb::set_branch_target |
| |
| // get the fallthrough and branch target successors; |
| // either of them can be NULL if they don't exist |
| LilBb* LilBb::get_fallthru() { |
| return fallthru; |
| } // LilBb::get_fallthru |
| |
| LilBb* LilBb::get_branch_target() { |
| return branch_target; |
| } // LilBb::get_branch_target |
| |
| |
| // gets the i'th predecessor (NULL if i >= num_pred) |
| LilBb *LilBb::get_pred(unsigned i) { |
| return (i < num_pred) ? pred[i] : NULL; |
| } // LilBb::get_pred |
| |
| // gets the next BB in the list |
| LilBb* LilBb::get_next() { |
| return next; |
| } // LilBb::get_next |
| |
| // returns whether this BB ends in a return instruction |
| // (tailcall implies return!) |
| bool LilBb::is_ret() { |
| return (end != NULL && |
| (end->tag == LIT_Ret || |
| (end->tag == LIT_Call && end->u.call.k == LCK_TailCall))); |
| } |
| |
| |
| // true if this BB contains calls (which means it may throw exceptions) |
| bool LilBb::does_calls() { |
| for (LilInstruction *i=start; i != NULL; i = i->next) { |
| if (i->tag == LIT_Call) |
| return true; |
| if (i == end) |
| break; |
| } |
| return false; |
| } |
| |
| |
| // true if this BB ends with a call.noret (which means it is probably |
| // cold code) |
| bool LilBb::does_call_noret() { |
| return (end != NULL && end->tag == LIT_Call && |
| end->u.call.k == LCK_CallNoRet); |
| } |
| |
| // find a BB with the specified label |
| // NULL if no such bb exists |
| LilBb* LilBb::get_by_label(LilCodeStub *cs, LilLabel l) { |
| assert(l != NULL); |
| LilBb *bb = cs->bb_list_head; |
| |
| while (bb != NULL && |
| (bb->get_label() == NULL || strcmp(bb->get_label(), l))) |
| bb = bb->next; |
| return bb; |
| } // LilBb::get_by_label |
| |
| // find a BB which contains the specified instruction |
| // NULL if no such BB exists |
| LilBb* LilBb::get_by_instruction(LilCodeStub *cs, LilInstruction *i) { |
| LilBb* bb = cs->bb_list_head; |
| |
| while (bb != NULL && !bb->contains(i)) |
| bb = bb->next; |
| |
| return bb; |
| } // LilBb::get_by_instruction |
| |
| // print a BB to a stream |
| // (does not print the BB's instructions) |
| void LilBb::print(FILE* out) { |
| fprintf(out, "-- BB %d ", id); |
| |
| // print predecessors |
| fprintf(out, "(pred:"); |
| if (num_pred == 0) |
| fprintf(out, " none)"); |
| else { |
| for (unsigned i=0; i<num_pred; i++) |
| fprintf(out, " %d", get_pred(i)->id); |
| fprintf(out, ")"); |
| } |
| |
| // print successors |
| fprintf(out, " (succ:"); |
| if (fallthru != NULL) |
| fprintf(out, " %d", fallthru->id); |
| if (branch_target != NULL) |
| fprintf(out, " %d", branch_target->id); |
| if (fallthru == NULL && branch_target == NULL) |
| fprintf(out, " none"); |
| fprintf(out, ")"); |
| |
| // print label |
| if (get_label() != NULL) |
| fprintf(out, " (label: %s)", get_label()); |
| |
| fprintf(out, " --\n"); |
| fflush(out); |
| } // void LilBb::print |
| |
| |
| // initializes the flowgraph by creating a list of BBs |
| // BBs in the list appear in the same order as they appear in the source code |
| void LilBb::init_fg(LilCodeStub *cs) { |
| LilInstructionIterator it(cs, true); |
| |
| LilInstruction *prev_inst = NULL; // previous instruction |
| LilBb* cur_bb = NULL; // current BB |
| |
| while (!it.at_end()) { |
| LilInstruction* inst = it.get_current(); |
| LilInstructionContext* ctxt = it.get_context(); |
| |
| if (inst->tag == LIT_Label || cur_bb == NULL) { |
| // close old bb, if it exists |
| if (cur_bb != NULL) { |
| assert(prev_inst != NULL); |
| cur_bb->set_last(prev_inst); |
| } |
| |
| // create new bb |
| cur_bb = new(*cs->my_memory) LilBb(cs, inst, ctxt); |
| cur_bb->id = cs->num_bbs++; |
| } |
| |
| // check if the BB should end here |
| if (inst->tag == LIT_J || |
| inst->tag == LIT_Jc || |
| inst->tag == LIT_Cas || |
| inst->tag == LIT_Ret || |
| (inst->tag == LIT_Call && inst->u.call.k != LCK_Call)) { |
| cur_bb->set_last(inst); |
| cur_bb = NULL; // so that the next inst will start a new one |
| } |
| |
| // advance pointers |
| prev_inst = inst; |
| it.goto_next(); |
| } |
| |
| // close the last BB |
| if (cur_bb != NULL) |
| cur_bb->set_last(prev_inst); |
| |
| // set up the successor / predecessor relations |
| for (cur_bb = cs->bb_list_head; cur_bb != NULL; cur_bb = cur_bb->next) { |
| LilInstruction *last_i = cur_bb->end; |
| assert(last_i != NULL); |
| if (last_i->tag == LIT_J) { |
| LilBb* succ = get_by_label(cs, last_i->u.j); |
| assert(succ != NULL); |
| cur_bb->set_branch_target(succ); |
| continue; // don't set fallthru |
| } |
| if (last_i->tag == LIT_Ret || |
| (last_i->tag == LIT_Call && last_i->u.call.k != LCK_Call)) { |
| continue; // terminal inst; don't set fallthru |
| } |
| if (last_i->tag == LIT_Jc) { |
| LilBb* succ = get_by_label(cs, last_i->u.jc.l); |
| assert(succ != NULL); |
| cur_bb->set_branch_target(succ); |
| } |
| if (last_i->tag == LIT_Cas) { |
| LilBb* succ = get_by_label(cs, last_i->u.ldst.l); |
| assert(succ); |
| cur_bb->set_branch_target(succ); |
| } |
| |
| // set fallthru |
| assert(cur_bb->next != NULL); |
| cur_bb->set_fallthru(cur_bb->next); |
| } |
| } // LilBb::init_fg |
| |
| |
| // EOF: lil.cpp |