提交 57780b7f 编写于 作者: T twisti

7192406: JSR 292: C2 needs exact return type information for invokedynamic and...

7192406: JSR 292: C2 needs exact return type information for invokedynamic and invokehandle call sites
Reviewed-by: kvn
上级 a31dc770
......@@ -1646,15 +1646,15 @@ Dependencies* GraphBuilder::dependency_recorder() const {
void GraphBuilder::invoke(Bytecodes::Code code) {
const bool is_invokedynamic = (code == Bytecodes::_invokedynamic);
bool will_link;
ciMethod* target = stream()->get_method(will_link);
ciSignature* declared_signature = NULL;
ciMethod* target = stream()->get_method(will_link, &declared_signature);
ciKlass* holder = stream()->get_declared_method_holder();
const Bytecodes::Code bc_raw = stream()->cur_bc_raw();
assert(declared_signature != NULL, "cannot be null");
// FIXME bail out for now
if ((bc_raw == Bytecodes::_invokehandle || is_invokedynamic) && !will_link) {
if (Bytecodes::has_optional_appendix(bc_raw) && !will_link) {
BAILOUT("unlinked call site (FIXME needs patching or recompile support)");
}
......@@ -1840,7 +1840,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
bool success = false;
if (target->is_method_handle_intrinsic()) {
// method handle invokes
success = for_method_handle_inline(target);
success = try_method_handle_inline(target);
} else {
// static binding => check if callee is ok
success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), code, better_receiver);
......@@ -1877,7 +1877,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
// inlining not successful => standard invoke
bool is_loaded = target->is_loaded();
ValueType* result_type = as_ValueType(target->return_type());
ValueType* result_type = as_ValueType(declared_signature->return_type());
ValueStack* state_before = copy_state_exhandling();
// The bytecode (code) might change in this method so we are checking this very late.
......@@ -3823,7 +3823,7 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecode
}
bool GraphBuilder::for_method_handle_inline(ciMethod* callee) {
bool GraphBuilder::try_method_handle_inline(ciMethod* callee) {
ValueStack* state_before = state()->copy_for_parsing();
vmIntrinsics::ID iid = callee->intrinsic_id();
switch (iid) {
......@@ -3858,7 +3858,7 @@ bool GraphBuilder::for_method_handle_inline(ciMethod* callee) {
// If the target is another method handle invoke try recursivly to get
// a better target.
if (target->is_method_handle_intrinsic()) {
if (for_method_handle_inline(target)) {
if (try_method_handle_inline(target)) {
return true;
}
} else {
......
......@@ -346,7 +346,7 @@ class GraphBuilder VALUE_OBJ_CLASS_SPEC {
const char* should_not_inline(ciMethod* callee) const;
// JSR 292 support
bool for_method_handle_inline(ciMethod* callee);
bool try_method_handle_inline(ciMethod* callee);
// helpers
void inline_bailout(const char* msg);
......
......@@ -236,12 +236,16 @@ void BCEscapeAnalyzer::invoke(StateInfo &state, Bytecodes::Code code, ciMethod*
ciInstanceKlass* callee_holder = ciEnv::get_instance_klass_for_declared_method_holder(holder);
ciInstanceKlass* actual_recv = callee_holder;
// some methods are obviously bindable without any type checks so
// convert them directly to an invokespecial.
// Some methods are obviously bindable without any type checks so
// convert them directly to an invokespecial or invokestatic.
if (target->is_loaded() && !target->is_abstract() && target->can_be_statically_bound()) {
switch (code) {
case Bytecodes::_invokevirtual: code = Bytecodes::_invokespecial; break;
case Bytecodes::_invokehandle: code = Bytecodes::_invokestatic; break;
case Bytecodes::_invokevirtual:
code = Bytecodes::_invokespecial;
break;
case Bytecodes::_invokehandle:
code = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokespecial;
break;
}
}
......@@ -826,8 +830,8 @@ void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, Growabl
break;
case Bytecodes::_getstatic:
case Bytecodes::_getfield:
{ bool will_link;
ciField* field = s.get_field(will_link);
{ bool ignored_will_link;
ciField* field = s.get_field(ignored_will_link);
BasicType field_type = field->type()->basic_type();
if (s.cur_bc() != Bytecodes::_getstatic) {
set_method_escape(state.apop());
......@@ -865,16 +869,21 @@ void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, Growabl
case Bytecodes::_invokestatic:
case Bytecodes::_invokedynamic:
case Bytecodes::_invokeinterface:
{ bool will_link;
ciMethod* target = s.get_method(will_link);
{ bool ignored_will_link;
ciSignature* declared_signature = NULL;
ciMethod* target = s.get_method(ignored_will_link, &declared_signature);
ciKlass* holder = s.get_declared_method_holder();
assert(declared_signature != NULL, "cannot be null");
// Push appendix argument, if one.
if (s.has_appendix()) {
state.apush(unknown_obj);
}
// Pass in raw bytecode because we need to see invokehandle instructions.
invoke(state, s.cur_bc_raw(), target, holder);
ciType* return_type = target->return_type();
// We are using the return type of the declared signature here because
// it might be a more concrete type than the one from the target (for
// e.g. invokedynamic and invokehandle).
ciType* return_type = declared_signature->return_type();
if (!return_type->is_primitive_type()) {
state.apush(unknown_obj);
} else if (return_type->is_one_word()) {
......
......@@ -738,7 +738,30 @@ methodOop ciEnv::lookup_method(instanceKlass* accessor,
ciMethod* ciEnv::get_method_by_index_impl(constantPoolHandle cpool,
int index, Bytecodes::Code bc,
ciInstanceKlass* accessor) {
int holder_index = cpool->klass_ref_index_at(index);
if (bc == Bytecodes::_invokedynamic) {
ConstantPoolCacheEntry* secondary_entry = cpool->cache()->secondary_entry_at(index);
const bool is_resolved = !secondary_entry->is_f1_null();
// FIXME: code generation could allow for null (unlinked) call site
// The call site could be made patchable as follows:
// Load the appendix argument from the constant pool.
// Test the appendix argument and jump to a known deopt routine if it is null.
// Jump through a patchable call site, which is initially a deopt routine.
// Patch the call site to the nmethod entry point of the static compiled lambda form.
// As with other two-component call sites, both values must be independently verified.
if (is_resolved) {
// Get the invoker methodOop and the extra argument from the constant pool.
methodOop adapter = secondary_entry->f2_as_vfinal_method();
return get_object(adapter)->as_method();
}
// Fake a method that is equivalent to a declared method.
ciInstanceKlass* holder = get_object(SystemDictionary::MethodHandle_klass())->as_instance_klass();
ciSymbol* name = ciSymbol::invokeBasic_name();
ciSymbol* signature = get_symbol(cpool->signature_ref_at(index));
return get_unloaded_method(holder, name, signature, accessor);
} else {
const int holder_index = cpool->klass_ref_index_at(index);
bool holder_is_accessible;
ciKlass* holder = get_klass_by_index_impl(cpool, holder_index, holder_is_accessible, accessor);
ciInstanceKlass* declared_holder = get_instance_klass_for_declared_method_holder(holder);
......@@ -759,7 +782,6 @@ ciMethod* ciEnv::get_method_by_index_impl(constantPoolHandle cpool,
case Bytecodes::_invokespecial:
case Bytecodes::_invokestatic:
{
oop appendix_oop = NULL;
methodOop m = constantPoolOopDesc::method_at_if_loaded(cpool, index);
if (m != NULL) {
return get_object(m)->as_method();
......@@ -790,39 +812,7 @@ ciMethod* ciEnv::get_method_by_index_impl(constantPoolHandle cpool,
ciSymbol* name = get_symbol(name_sym);
ciSymbol* signature = get_symbol(sig_sym);
return get_unloaded_method(declared_holder, name, signature, accessor);
}
// ------------------------------------------------------------------
// ciEnv::get_fake_invokedynamic_method_impl
ciMethod* ciEnv::get_fake_invokedynamic_method_impl(constantPoolHandle cpool,
int index, Bytecodes::Code bc,
ciInstanceKlass* accessor) {
// Compare the following logic with InterpreterRuntime::resolve_invokedynamic.
assert(bc == Bytecodes::_invokedynamic, "must be invokedynamic");
ConstantPoolCacheEntry* secondary_entry = cpool->cache()->secondary_entry_at(index);
bool is_resolved = !secondary_entry->is_f1_null();
// FIXME: code generation could allow for null (unlinked) call site
// The call site could be made patchable as follows:
// Load the appendix argument from the constant pool.
// Test the appendix argument and jump to a known deopt routine if it is null.
// Jump through a patchable call site, which is initially a deopt routine.
// Patch the call site to the nmethod entry point of the static compiled lambda form.
// As with other two-component call sites, both values must be independently verified.
// Call site might not be resolved yet.
// Stop the code path here with an unlinked method.
if (!is_resolved) {
ciInstanceKlass* holder = get_object(SystemDictionary::MethodHandle_klass())->as_instance_klass();
ciSymbol* name = ciSymbol::invokeBasic_name();
ciSymbol* signature = get_symbol(cpool->signature_ref_at(index));
return get_unloaded_method(holder, name, signature, accessor);
}
// Get the invoker methodOop and the extra argument from the constant pool.
methodOop adapter = secondary_entry->f2_as_vfinal_method();
return get_object(adapter)->as_method();
}
......@@ -853,11 +843,7 @@ ciInstanceKlass* ciEnv::get_instance_klass_for_declared_method_holder(ciKlass* m
ciMethod* ciEnv::get_method_by_index(constantPoolHandle cpool,
int index, Bytecodes::Code bc,
ciInstanceKlass* accessor) {
if (bc == Bytecodes::_invokedynamic) {
GUARDED_VM_ENTRY(return get_fake_invokedynamic_method_impl(cpool, index, bc, accessor);)
} else {
GUARDED_VM_ENTRY(return get_method_by_index_impl( cpool, index, bc, accessor);)
}
GUARDED_VM_ENTRY(return get_method_by_index_impl(cpool, index, bc, accessor);)
}
......
......@@ -152,9 +152,6 @@ private:
ciMethod* get_method_by_index_impl(constantPoolHandle cpool,
int method_index, Bytecodes::Code bc,
ciInstanceKlass* loading_klass);
ciMethod* get_fake_invokedynamic_method_impl(constantPoolHandle cpool,
int index, Bytecodes::Code bc,
ciInstanceKlass* accessor);
// Helper methods
bool check_klass_accessibility(ciKlass* accessing_klass,
......
......@@ -1215,9 +1215,10 @@ void ciMethod::print_impl(outputStream* st) {
holder()->print_name_on(st);
st->print(" signature=");
signature()->as_symbol()->print_symbol_on(st);
st->print(" arg_size=%d", arg_size());
if (is_loaded()) {
st->print(" loaded=true flags=");
st->print(" loaded=true");
st->print(" arg_size=%d", arg_size());
st->print(" flags=");
flags().print_member_flags(st);
} else {
st->print(" loaded=false");
......
......@@ -355,11 +355,23 @@ int ciBytecodeStream::get_method_index() {
// ciBytecodeStream::get_method
//
// If this is a method invocation bytecode, get the invoked method.
ciMethod* ciBytecodeStream::get_method(bool& will_link) {
// Additionally return the declared signature to get more concrete
// type information if required (Cf. invokedynamic and invokehandle).
ciMethod* ciBytecodeStream::get_method(bool& will_link, ciSignature* *declared_signature_result) {
VM_ENTRY_MARK;
ciEnv* env = CURRENT_ENV;
constantPoolHandle cpool(_method->get_methodOop()->constants());
ciMethod* m = CURRENT_ENV->get_method_by_index(cpool, get_method_index(), cur_bc(), _holder);
ciMethod* m = env->get_method_by_index(cpool, get_method_index(), cur_bc(), _holder);
will_link = m->is_loaded();
// Get declared method signature and return it.
if (has_optional_appendix()) {
const int sig_index = get_method_signature_index();
Symbol* sig_sym = cpool->symbol_at(sig_index);
ciKlass* pool_holder = env->get_object(cpool->pool_holder())->as_klass();
(*declared_signature_result) = new (env->arena()) ciSignature(pool_holder, cpool, env->get_symbol(sig_sym));
} else {
(*declared_signature_result) = m->signature();
}
return m;
}
......@@ -418,24 +430,6 @@ int ciBytecodeStream::get_method_holder_index() {
return cpool->klass_ref_index_at(get_method_index());
}
// ------------------------------------------------------------------
// ciBytecodeStream::get_declared_method_signature
//
// Get the declared signature of the currently referenced method.
//
// This is always the same as the signature of the resolved method
// itself, except for _invokehandle and _invokedynamic calls.
//
ciSignature* ciBytecodeStream::get_declared_method_signature() {
int sig_index = get_method_signature_index();
VM_ENTRY_MARK;
ciEnv* env = CURRENT_ENV;
constantPoolHandle cpool(_method->get_methodOop()->constants());
Symbol* sig_sym = cpool->symbol_at(sig_index);
ciKlass* pool_holder = env->get_object(cpool->pool_holder())->as_klass();
return new (env->arena()) ciSignature(pool_holder, cpool, env->get_symbol(sig_sym));
}
// ------------------------------------------------------------------
// ciBytecodeStream::get_method_signature_index
//
......@@ -443,11 +437,12 @@ ciSignature* ciBytecodeStream::get_declared_method_signature() {
// referenced by the current bytecode. Used for generating
// deoptimization information.
int ciBytecodeStream::get_method_signature_index() {
VM_ENTRY_MARK;
GUARDED_VM_ENTRY(
constantPoolOop cpool = _holder->get_instanceKlass()->constants();
int method_index = get_method_index();
int name_and_type_index = cpool->name_and_type_ref_index_at(method_index);
const int method_index = get_method_index();
const int name_and_type_index = cpool->name_and_type_ref_index_at(method_index);
return cpool->signature_ref_index_at(name_and_type_index);
)
}
// ------------------------------------------------------------------
......
......@@ -151,6 +151,8 @@ public:
// Does this instruction contain an index which refes into the CP cache?
bool has_cache_index() const { return Bytecodes::uses_cp_cache(cur_bc_raw()); }
bool has_optional_appendix() { return Bytecodes::has_optional_appendix(cur_bc_raw()); }
int get_index_u1() const {
return bytecode().get_index_u1(cur_bc_raw());
}
......@@ -257,13 +259,11 @@ public:
int get_field_holder_index();
int get_field_signature_index();
// If this is a method invocation bytecode, get the invoked method.
ciMethod* get_method(bool& will_link);
ciMethod* get_method(bool& will_link, ciSignature* *declared_signature_result);
bool has_appendix();
ciObject* get_appendix();
ciKlass* get_declared_method_holder();
int get_method_holder_index();
ciSignature* get_declared_method_signature();
int get_method_signature_index();
ciCPCache* get_cpcache() const;
......
......@@ -643,9 +643,11 @@ void ciTypeFlow::StateVector::do_getstatic(ciBytecodeStream* str) {
// ------------------------------------------------------------------
// ciTypeFlow::StateVector::do_invoke
void ciTypeFlow::StateVector::do_invoke(ciBytecodeStream* str,
bool has_receiver_foo) {
bool has_receiver) {
bool will_link;
ciMethod* callee = str->get_method(will_link);
ciSignature* declared_signature = NULL;
ciMethod* callee = str->get_method(will_link, &declared_signature);
assert(declared_signature != NULL, "cannot be null");
if (!will_link) {
// We weren't able to find the method.
if (str->cur_bc() == Bytecodes::_invokedynamic) {
......@@ -658,22 +660,12 @@ void ciTypeFlow::StateVector::do_invoke(ciBytecodeStream* str,
trap(str, unloaded_holder, str->get_method_holder_index());
}
} else {
// TODO Use Bytecode_invoke after metadata changes.
//Bytecode_invoke inv(str->method(), str->cur_bci());
//const bool has_receiver = callee->is_loaded() ? !callee->is_static() : inv.has_receiver();
Bytecode inv(str);
Bytecodes::Code code = inv.invoke_code();
const bool has_receiver = callee->is_loaded() ? !callee->is_static() : code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic;
ciSignature* signature = callee->signature();
ciSignatureStream sigstr(signature);
// Push appendix argument, if one.
if (str->has_appendix()) {
ciObject* appendix = str->get_appendix();
push_object(appendix->klass());
}
int arg_size = signature->size();
int stack_base = stack_size() - arg_size;
// We are using the declared signature here because it might be
// different from the callee signature (Cf. invokedynamic and
// invokehandle).
ciSignatureStream sigstr(declared_signature);
const int arg_size = declared_signature->size();
const int stack_base = stack_size() - arg_size;
int i = 0;
for( ; !sigstr.at_return_type(); sigstr.next()) {
ciType* type = sigstr.type();
......@@ -689,7 +681,6 @@ void ciTypeFlow::StateVector::do_invoke(ciBytecodeStream* str,
for (int j = 0; j < arg_size; j++) {
pop();
}
assert(!callee->is_loaded() || has_receiver == !callee->is_static(), "mismatch");
if (has_receiver) {
// Check this?
pop_object();
......
......@@ -424,6 +424,8 @@ class Bytecodes: AllStatic {
|| code == _fconst_0 || code == _dconst_0); }
static bool is_invoke (Code code) { return (_invokevirtual <= code && code <= _invokedynamic); }
static bool has_optional_appendix(Code code) { return code == _invokedynamic || code == _invokehandle; }
static int compute_flags (const char* format, int more_flags = 0); // compute the flags
static int flags (int code, bool is_wide) {
assert(code == (u_char)code, "must be a byte");
......
......@@ -341,25 +341,26 @@ void Parse::do_call() {
kill_dead_locals();
// Set frequently used booleans
bool is_virtual = bc() == Bytecodes::_invokevirtual;
bool is_virtual_or_interface = is_virtual || bc() == Bytecodes::_invokeinterface;
bool has_receiver = is_virtual_or_interface || bc() == Bytecodes::_invokespecial;
bool is_invokedynamic = bc() == Bytecodes::_invokedynamic;
const bool is_virtual = bc() == Bytecodes::_invokevirtual;
const bool is_virtual_or_interface = is_virtual || bc() == Bytecodes::_invokeinterface;
const bool has_receiver = is_virtual_or_interface || bc() == Bytecodes::_invokespecial;
// Find target being called
bool will_link;
ciMethod* bc_callee = iter().get_method(will_link); // actual callee from bytecode
ciInstanceKlass* holder_klass = bc_callee->holder();
ciSignature* declared_signature = NULL;
ciMethod* orig_callee = iter().get_method(will_link, &declared_signature); // callee in the bytecode
ciInstanceKlass* holder_klass = orig_callee->holder();
ciKlass* holder = iter().get_declared_method_holder();
ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder);
assert(declared_signature != NULL, "cannot be null");
// uncommon-trap when callee is unloaded, uninitialized or will not link
// bailout when too many arguments for register representation
if (!will_link || can_not_compile_call_site(bc_callee, klass)) {
if (!will_link || can_not_compile_call_site(orig_callee, klass)) {
#ifndef PRODUCT
if (PrintOpto && (Verbose || WizardMode)) {
method()->print_name(); tty->print_cr(" can not compile call at bci %d to:", bci());
bc_callee->print_name(); tty->cr();
orig_callee->print_name(); tty->cr();
}
#endif
return;
......@@ -372,7 +373,7 @@ void Parse::do_call() {
// Note: In the absence of miranda methods, an abstract class K can perform
// an invokevirtual directly on an interface method I.m if K implements I.
const int nargs = bc_callee->arg_size();
const int nargs = orig_callee->arg_size();
// Push appendix argument (MethodType, CallSite, etc.), if one.
if (iter().has_appendix()) {
......@@ -392,13 +393,13 @@ void Parse::do_call() {
// Choose call strategy.
bool call_is_virtual = is_virtual_or_interface;
int vtable_index = methodOopDesc::invalid_vtable_index;
ciMethod* callee = bc_callee;
ciMethod* callee = orig_callee;
// Try to get the most accurate receiver type
if (is_virtual_or_interface) {
Node* receiver_node = stack(sp() - nargs);
const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr();
ciMethod* optimized_virtual_method = optimize_inlining(method(), bci(), klass, bc_callee, receiver_type);
ciMethod* optimized_virtual_method = optimize_inlining(method(), bci(), klass, orig_callee, receiver_type);
// Have the call been sufficiently improved such that it is no longer a virtual?
if (optimized_virtual_method != NULL) {
......@@ -425,7 +426,8 @@ void Parse::do_call() {
// It decides whether inlining is desirable or not.
CallGenerator* cg = C->call_generator(callee, vtable_index, call_is_virtual, jvms, try_inline, prof_factor());
bc_callee = callee = NULL; // don't use bc_callee and callee after this point
// NOTE: Don't use orig_callee and callee after this point! Use cg->method() instead.
orig_callee = callee = NULL;
// ---------------------
// Round double arguments before call
......@@ -497,9 +499,9 @@ void Parse::do_call() {
round_double_result(cg->method());
ciType* rtype = cg->method()->return_type();
if (iter().cur_bc_raw() == Bytecodes::_invokehandle || is_invokedynamic) {
if (Bytecodes::has_optional_appendix(iter().cur_bc_raw())) {
// Be careful here with return types.
ciType* ctype = iter().get_declared_method_signature()->return_type();
ciType* ctype = declared_signature->return_type();
if (ctype != rtype) {
BasicType rt = rtype->basic_type();
BasicType ct = ctype->basic_type();
......@@ -528,15 +530,13 @@ void Parse::do_call() {
} else if (rt == T_OBJECT || rt == T_ARRAY) {
assert(ct == T_OBJECT || ct == T_ARRAY, err_msg_res("rt=%s, ct=%s", type2name(rt), type2name(ct)));
if (ctype->is_loaded()) {
Node* if_fail = top();
retnode = gen_checkcast(retnode, makecon(TypeKlassPtr::make(ctype->as_klass())), &if_fail);
if (if_fail != top()) {
PreserveJVMState pjvms(this);
set_control(if_fail);
builtin_throw(Deoptimization::Reason_class_check);
}
const TypeOopPtr* arg_type = TypeOopPtr::make_from_klass(rtype->as_klass());
const Type* sig_type = TypeOopPtr::make_from_klass(ctype->as_klass());
if (arg_type != NULL && !arg_type->higher_equal(sig_type)) {
Node* cast_obj = _gvn.transform(new (C, 2) CheckCastPPNode(control(), retnode, sig_type));
pop();
push(retnode);
push(cast_obj);
}
}
} else {
assert(ct == rt, err_msg_res("unexpected mismatch rt=%d, ct=%d", rt, ct));
......
......@@ -1006,11 +1006,11 @@ bool GraphKit::compute_stack_effects(int& inputs, int& depth, bool for_parse) {
case Bytecodes::_putfield:
{
bool is_get = (depth >= 0), is_static = (depth & 1);
bool ignore;
ciBytecodeStream iter(method());
iter.reset_to_bci(bci());
iter.next();
ciField* field = iter.get_field(ignore);
bool ignored_will_link;
ciField* field = iter.get_field(ignored_will_link);
int size = field->type()->size();
inputs = (is_static ? 0 : 1);
if (is_get) {
......@@ -1028,11 +1028,13 @@ bool GraphKit::compute_stack_effects(int& inputs, int& depth, bool for_parse) {
case Bytecodes::_invokedynamic:
case Bytecodes::_invokeinterface:
{
bool ignore;
ciBytecodeStream iter(method());
iter.reset_to_bci(bci());
iter.next();
ciMethod* callee = iter.get_method(ignore);
bool ignored_will_link;
ciSignature* declared_signature = NULL;
ciMethod* callee = iter.get_method(ignored_will_link, &declared_signature);
assert(declared_signature != NULL, "cannot be null");
// (Do not use ciMethod::arg_size(), because
// it might be an unloaded method, which doesn't
// know whether it is static or not.)
......@@ -1046,7 +1048,7 @@ bool GraphKit::compute_stack_effects(int& inputs, int& depth, bool for_parse) {
// remove any appendix arguments that were popped.
inputs = callee->invoke_arg_size(code) - (callee->has_member_arg() ? 1 : 0);
}
int size = callee->return_type()->size();
int size = declared_signature->return_type()->size();
depth = size - inputs;
}
break;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册