void SCCP::analyzeSSA(Instr* instr) { if (numDefs(instr) == 0) { if (isBlockEnd(instr)) analyzeBranch((BlockEndInstr*)instr); return; } // dev: save current types for later check int i = 0; for (ArrayRange<Def> d = defRange(instr); !d.empty(); d.popFront(), ++i) old_types[i] = type(d.front()); // compute types eval_counts[instr->id]++; analyzer.computeTypes(instr); // dev: check computation result bool changed2 = false; i = 0; for (ArrayRange<Def> d = defRange(instr); !d.empty(); d.popFront(), ++i) { AvmAssert(subtypeof(old_types[i], type(d.front())) && "illegal type narrowing"); changed2 |= *type(d.front()) != *old_types[i]; } if (changed2) { if (enable_typecheck) { printInstr(instr); } addInstrUsers(instr); } }
Def* IdentityAnalyzer::do_coerce(BinaryStmt* instr) { const Type* traits_type = type(instr->lhs_in()); if (isConst(traits_type)) { const Type* to_type = lattice_->makeType(traitsVal(traits_type)); Def* value_in = def(instr->rhs_in()); if (subtypeof(type(value_in), to_type)) return identity(instr, value_in); } return def_; }
Def* IdentityAnalyzer::do_cast(BinaryStmt* instr) { const Type* traits_type = type(instr->lhs_in()); if (!isConst(traits_type)) return def_; // Don't know the target traits. const Type* to_type = lattice_->makeType(traitsVal(traits_type)); // fixme: this is copied from coerceIdentity(). Def* value_in = def(instr->rhs_in()); if (subtypeof(type(value_in), to_type)) return identity(instr, value_in); return def_; }
/* This function is the top level implementation of the instanceof operator. This is what * will actually get called by the program. * object: a pointer to the object being tested * isa_class: a pointer to the info of the class or interface which we are asking about * isa_type_args: type arguments for the class or interface, stored in the same depth-first * format as the object. */ bool wrt_instanceof(const w_object* object, const w_class_info* isa_class, const w_class_info* const* isa_type_args) { const w_class_info* obj_class = object->vtable->info; intptr_t obj_type_args_offset = obj_class->instance_size / sizeof(w_class_info*) - 1; const w_class_info* const* obj_type_args = object->type_args + obj_type_args_offset; intptr_t obj_type_args_size = find_type_args_size(obj_class->type_parameters.size, obj_type_args); intptr_t* obj_type_args_index = alloca(obj_type_args_size * sizeof(intptr_t)); index_type_args(obj_class->type_parameters.size, obj_type_args, obj_type_args_index, NULL); intptr_t isa_type_args_size = find_type_args_size(isa_class->type_parameters.size, isa_type_args); intptr_t* isa_type_args_index = alloca(isa_type_args_size * sizeof(intptr_t)); index_type_args(isa_class->type_parameters.size, isa_type_args, isa_type_args_index, NULL); return subtypeof(obj_class, obj_type_args, obj_type_args_index, isa_class, isa_type_args, isa_type_args_index); }
Def* IdentityAnalyzer::coerceIdentity(UnaryStmt* instr, const Type* to_type) { Def* value_in = def(instr->value_in()); if (subtypeof(type(value_in), to_type)) return identity(instr, value_in); return def_; }
/* Test whether one object type S is a subtype of another T. * {s,t}_class: the class or interface of the types * {s,t}_type_args: a list of type argument words for S and T, stored in depth-first format * {s,t}_type_args_index: an index used to navigate the type argument words. See index_type_args. */ static bool subtypeof(const w_class_info* s_class, const w_class_info* const* s_type_args, const intptr_t* s_type_args_index, const w_class_info* t_class, const w_class_info* const* t_type_args, const intptr_t* t_type_args_index) { if (s_class == NULL) { // Nothing is a subtype of everything return true; } else if (t_class == NULL) { // There is no subtype of nothing return false; } else if (s_class != t_class) { // If S and T are of different classes, we can't compare them directly. We need to see if // S is a subtype of another type, S', which IS of the same class as T. Every class_info // and interface_info has a list of supertypes and an array of instructions for converting // to each supertype. // Determine whether we even support the other type intptr_t supertype_index = find_supertype(s_class, t_class); if (supertype_index == s_class->supertype_count) return false; // If we do support the other type, we need to convert S to that type. If there are no // instructions for doing so, T must have no reified type parameters, so we can stop. if (s_class->supertype_instructions == NULL) return true; const int8_t* const* supertype_instructions = s_class->supertype_instructions[supertype_index]; if (supertype_instructions == NULL) return true; // Do the actual conversion. We create a small array of offsets to the end of each // type argument so we can easily move whole arguments. We create a new array of type // arguments of the appropriate size and execute the instructions. intptr_t s_type_arg_count = s_class->type_parameters.size; intptr_t* s_type_arg_offsets = alloca(s_type_arg_count * sizeof(intptr_t)); intptr_t current_offset = 0; for (intptr_t i = 0; i < s_type_arg_count; i++) { current_offset += s_type_args_index[current_offset]; s_type_arg_offsets[i] = current_offset; } intptr_t super_type_args_size = find_super_type_args_size(supertype_instructions, s_type_arg_offsets); const w_class_info** super_type_args = alloca(super_type_args_size * sizeof(w_class_info*)); execute_supertype_instructions(supertype_instructions, s_type_args, s_type_arg_offsets, super_type_args); // We need to create a new index for the converted type arguments. This can probably // be optimized to be done at the same time. intptr_t* super_type_args_index = alloca(super_type_args_size * sizeof(intptr_t)); index_type_args(t_class->type_parameters.size, super_type_args, super_type_args_index, NULL); s_class = t_class; s_type_args = super_type_args; s_type_args_index = super_type_args_index; } // At this point, S and T are of the same class and will have the same number of type // arguments. We just need to compare each pair of type arguments according to the variance // of the corresponding parameter. const w_type_parameter_info* tp_info = (const w_type_parameter_info*) t_class->type_parameters.data; intptr_t next_s = 0, next_t = 0; for (intptr_t i = 0; i < t_class->type_parameters.size; ++i) { const w_class_info* tp_s_class = s_type_args[next_s]; const w_class_info* const* tp_s_type_args = s_type_args + next_s + 1; const intptr_t* tp_s_type_args_index = s_type_args_index + next_s + 1; next_s += s_type_args_index[next_s]; const w_class_info* tp_t_class = t_type_args[next_t]; const w_class_info* const* tp_t_type_args = t_type_args + next_t + 1; const intptr_t* tp_t_type_args_index = t_type_args_index + next_t + 1; next_t += t_type_args_index[next_t]; uint32_t variance = tp_info[i].flags & TYPE_PARAMETER_INFO_VARIANCE_MASK; if (variance == TYPE_PARAMETER_INFO_INVARIANT_FLAG) { if(!equals(tp_s_class, tp_s_type_args, tp_s_type_args_index, tp_t_class, tp_t_type_args, tp_t_type_args_index)) return false; } else if (variance == TYPE_PARAMETER_INFO_COVARIANT_FLAG) { if (!subtypeof(tp_s_class, tp_s_type_args, tp_s_type_args_index, tp_t_class, tp_t_type_args, tp_t_type_args_index)) return false; } else { // variance == TYPE_PARAMETER_INFO_CONTRAVARIANT_FLAG if (!subtypeof(tp_t_class, tp_t_type_args, tp_t_type_args_index, tp_s_class, tp_s_type_args, tp_s_type_args_index)) return false; } } return true; }