From edd9a63b390a6e7d11c2a359a80b2ec28adbe06c Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Tue, 21 Sep 2021 14:48:41 -0400 Subject: [PATCH 01/11] More bcReachabilityAnalzyer changes Now walking the blocks 1-by-1 and recording the classes. TODO: * Remove the printfs * expose the _discovered_classes to the user * expose the enqueuedMethods to the user * track the invoking bytecode for enqueued methods --- .../share/ci/bcReachabilityAnalyzer.cpp | 592 ++++++++++++++++++ .../share/ci/bcReachabilityAnalyzer.hpp | 22 + 2 files changed, 614 insertions(+) diff --git a/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp b/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp index 0d62593799d17..6576bdac62887 100644 --- a/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp +++ b/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp @@ -1,6 +1,598 @@ #include "ci/bcReachabilityAnalyzer.hpp" +#include "ci/ciConstant.hpp" +#include "ci/ciField.hpp" +#include "ci/ciMethodBlocks.hpp" +#include "ci/ciStreams.hpp" +#include "ci/ciUtilities.hpp" + BCReachabilityAnalyzer::BCReachabilityAnalyzer(ciMethod* method, BCReachabilityAnalyzer* parent) + : _arena(CURRENT_ENV->arena()) + , _method(method) + , _methodBlocks(nullptr) + , _parent(parent) + , _discovered_klasses(_arena, 4, 0, NULL) { // TODO +} + +void BCReachabilityAnalyzer::iterate_blocks() { + int numblocks = _methodBlocks->num_blocks(); + int stkSize = _method->max_stack(); + int numLocals = _method->max_locals(); + + GrowableArray worklist(_arena, numblocks / 4, 0, NULL); + GrowableArray successors(_arena, 4, 0, NULL); + + _methodBlocks->clear_processed(); + + // initialize block 0 state from method signature + ciSignature* sig = _method->signature(); + ciBlock* first_blk = _methodBlocks->block_containing(0); + int fb_i = first_blk->index(); + for (int i = 0; i < sig->count(); i++) { + ciType* t = sig->type_at(i); + if (!t->is_primitive_type()) { + recordReferencedType(t); + } + } + + worklist.push(first_blk); + while(worklist.length() > 0) { + ciBlock *blk = worklist.pop(); + if (blk->is_handler() || blk->is_ret_target()) { + // for an exception handler or a target of a ret instruction, we assume the worst case, + // that any variable could contain any argument + + } else { + + } + iterate_one_block(blk, successors); + // if this block has any exception handlers, push them + // onto successor list + if (blk->has_handler()) { + DEBUG_ONLY(int handler_count = 0;) + int blk_start = blk->start_bci(); + int blk_end = blk->limit_bci(); + for (int i = 0; i < numblocks; i++) { + ciBlock *b = _methodBlocks->block(i); + if (b->is_handler()) { + int ex_start = b->ex_start_bci(); + int ex_end = b->ex_limit_bci(); + if ((ex_start >= blk_start && ex_start < blk_end) || + (ex_end > blk_start && ex_end <= blk_end)) { + successors.push(b); + } + DEBUG_ONLY(handler_count++;) + } + } + assert(handler_count > 0, "must find at least one handler"); + } + // merge computed variable state with successors + while(successors.length() > 0) { + ciBlock *succ = successors.pop(); + // merge_block_states(blockstates, succ, &state); + if (!succ->processed()) + worklist.push(succ); + } + } +} + +// based on BCEscapeAnalyzer +void BCReachabilityAnalyzer::iterate_one_block(ciBlock *blk, GrowableArray &successors) { + blk->set_processed(); + ciBytecodeStream s(_method); + int limit_bci = blk->limit_bci(); + bool fall_through = false; + + s.reset_to_bci(blk->start_bci()); + while (s.next() != ciBytecodeStream::EOBC() && s.cur_bci() < limit_bci) { + fall_through = true; + printf("."); + switch (s.cur_bc()) { + case Bytecodes::_nop: + case Bytecodes::_aconst_null: + case Bytecodes::_iconst_m1: + case Bytecodes::_iconst_0: + case Bytecodes::_iconst_1: + case Bytecodes::_iconst_2: + case Bytecodes::_iconst_3: + case Bytecodes::_iconst_4: + case Bytecodes::_iconst_5: + case Bytecodes::_fconst_0: + case Bytecodes::_fconst_1: + case Bytecodes::_fconst_2: + case Bytecodes::_bipush: + case Bytecodes::_sipush: + case Bytecodes::_lconst_0: + case Bytecodes::_lconst_1: + case Bytecodes::_dconst_0: + case Bytecodes::_dconst_1: + /* No ops */ + break; + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + { + // Avoid calling get_constant() which will try to allocate + // unloaded constant. We need only constant's type. + int index = s.get_constant_pool_index(); + constantTag tag = s.get_constant_pool_tag(index); + if (tag.is_long() || tag.is_double()) { + // do nothing + } else { + ciConstant constant = s.get_constant(); + recordReferencedType(constant.as_object()->klass()); + } + break; + } + case Bytecodes::_aload: + case Bytecodes::_iload: + case Bytecodes::_fload: + case Bytecodes::_iload_0: + case Bytecodes::_iload_1: + case Bytecodes::_iload_2: + case Bytecodes::_iload_3: + case Bytecodes::_fload_0: + case Bytecodes::_fload_1: + case Bytecodes::_fload_2: + case Bytecodes::_fload_3: + case Bytecodes::_lload: + case Bytecodes::_dload: + case Bytecodes::_lload_0: + case Bytecodes::_lload_1: + case Bytecodes::_lload_2: + case Bytecodes::_lload_3: + case Bytecodes::_dload_0: + case Bytecodes::_dload_1: + case Bytecodes::_dload_2: + case Bytecodes::_dload_3: + case Bytecodes::_aload_0: + case Bytecodes::_aload_1: + case Bytecodes::_aload_2: + case Bytecodes::_aload_3: + case Bytecodes::_iaload: + case Bytecodes::_faload: + case Bytecodes::_baload: + case Bytecodes::_caload: + case Bytecodes::_saload: + case Bytecodes::_laload: + case Bytecodes::_daload: + /* No ops */ + break; + case Bytecodes::_aaload: + { // TODO + } + break; + case Bytecodes::_istore: + case Bytecodes::_fstore: + case Bytecodes::_istore_0: + case Bytecodes::_istore_1: + case Bytecodes::_istore_2: + case Bytecodes::_istore_3: + case Bytecodes::_fstore_0: + case Bytecodes::_fstore_1: + case Bytecodes::_fstore_2: + case Bytecodes::_fstore_3: + case Bytecodes::_lstore: + case Bytecodes::_dstore: + case Bytecodes::_lstore_0: + case Bytecodes::_lstore_1: + case Bytecodes::_lstore_2: + case Bytecodes::_lstore_3: + case Bytecodes::_dstore_0: + case Bytecodes::_dstore_1: + case Bytecodes::_dstore_2: + case Bytecodes::_dstore_3: + case Bytecodes::_astore: + case Bytecodes::_astore_0: + case Bytecodes::_astore_1: + case Bytecodes::_astore_2: + case Bytecodes::_astore_3: + case Bytecodes::_iastore: + case Bytecodes::_fastore: + case Bytecodes::_bastore: + case Bytecodes::_castore: + case Bytecodes::_sastore: + case Bytecodes::_lastore: + case Bytecodes::_dastore: + /* No op */ + break; + case Bytecodes::_aastore: + { + // TODO? + break; + } + case Bytecodes::_pop: + case Bytecodes::_pop2: + case Bytecodes::_dup: + case Bytecodes::_dup_x1: + case Bytecodes::_dup_x2: + case Bytecodes::_dup2: + case Bytecodes::_dup2_x1: + case Bytecodes::_dup2_x2: + case Bytecodes::_swap: + case Bytecodes::_iadd: + case Bytecodes::_fadd: + case Bytecodes::_isub: + case Bytecodes::_fsub: + case Bytecodes::_imul: + case Bytecodes::_fmul: + case Bytecodes::_idiv: + case Bytecodes::_fdiv: + case Bytecodes::_irem: + case Bytecodes::_frem: + case Bytecodes::_iand: + case Bytecodes::_ior: + case Bytecodes::_ixor: + case Bytecodes::_ladd: + case Bytecodes::_dadd: + case Bytecodes::_lsub: + case Bytecodes::_dsub: + case Bytecodes::_lmul: + case Bytecodes::_dmul: + case Bytecodes::_ldiv: + case Bytecodes::_ddiv: + case Bytecodes::_lrem: + case Bytecodes::_drem: + case Bytecodes::_land: + case Bytecodes::_lor: + case Bytecodes::_lxor: + case Bytecodes::_ishl: + case Bytecodes::_ishr: + case Bytecodes::_iushr: + case Bytecodes::_lshl: + case Bytecodes::_lshr: + case Bytecodes::_lushr: + case Bytecodes::_ineg: + case Bytecodes::_fneg: + case Bytecodes::_lneg: + case Bytecodes::_dneg: + case Bytecodes::_iinc: + case Bytecodes::_i2l: + case Bytecodes::_i2d: + case Bytecodes::_f2l: + case Bytecodes::_f2d: + case Bytecodes::_i2f: + case Bytecodes::_f2i: + case Bytecodes::_l2i: + case Bytecodes::_l2f: + case Bytecodes::_d2i: + case Bytecodes::_d2f: + case Bytecodes::_l2d: + case Bytecodes::_d2l: + case Bytecodes::_i2b: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + case Bytecodes::_lcmp: + case Bytecodes::_dcmpl: + case Bytecodes::_dcmpg: + case Bytecodes::_fcmpl: + case Bytecodes::_fcmpg: + /* no op */ + break; + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_goto: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_jsr: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_ret: + // we don't track the destination of a "ret" instruction + assert(s.next_bci() == limit_bci, "branch must end block"); + fall_through = false; + break; + case Bytecodes::_return: + assert(s.next_bci() == limit_bci, "return must end block"); + fall_through = false; + break; + case Bytecodes::_tableswitch: + { + Bytecode_tableswitch sw(&s); + int len = sw.length(); + int dest_bci; + for (int i = 0; i < len; i++) { + dest_bci = s.cur_bci() + sw.dest_offset_at(i); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + } + dest_bci = s.cur_bci() + sw.default_offset(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + assert(s.next_bci() == limit_bci, "branch must end block"); + fall_through = false; + break; + } + case Bytecodes::_lookupswitch: + { + Bytecode_lookupswitch sw(&s); + int len = sw.number_of_pairs(); + int dest_bci; + for (int i = 0; i < len; i++) { + dest_bci = s.cur_bci() + sw.pair_at(i).offset(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + } + dest_bci = s.cur_bci() + sw.default_offset(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_ireturn: + case Bytecodes::_freturn: + case Bytecodes::_lreturn: + case Bytecodes::_dreturn: + /* no op */ + break; + case Bytecodes::_areturn: + // TODO: need to do something? + fall_through = false; + break; + case Bytecodes::_getfield: + case Bytecodes::_putfield: + // Fall through to the static case for now and for + // the loading of the canonical holder of the field. + // Eventually, we may be able to further optimize this + // not load instance field-related classes as we won't + // have an instance to operate on if the class is never + // allocated. + // TODO: re-examine fall through here in the future and + // potentially optimize the instance case. + case Bytecodes::_getstatic: + case Bytecodes::_putstatic: + { + // For a static field, we need to ensure that the + // declared class is loaded. This code counts on + // actual loding of the class to ensure that all + // supers{class, interfaces} will be loaded as per + // the spec. + // + // We don't force the loading of the declared type + // of the field as we won't necessarily have an + // instance of the class at runtime. No need to + // force it loaded if we don't know it will ever + // be allocated. + bool ignored_will_link; + ciField* field = s.get_field(ignored_will_link); + recordReferencedType(field->holder()); + } + break; + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokedynamic: + case Bytecodes::_invokeinterface: + + { 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(); + if (true || target->is_static()) { + // Enqueue the class that declares the method as we need to have + // it loaded. For non-static invokes, we need an instance for + // first for the class to be worth loading. + // BUT we won't revisit every method every time a class is loaded. + // Be less conservative and allow additional class loads on the assumption + // we can remove them later. + recordReferencedType(holder); + } + assert(declared_signature != NULL, "cannot be null"); + // TODO: likely have to pass the bytecode in as well to track the + // kind of invoke + enqueueMethod(target); + } + #if 0 + // If the current bytecode has an attached appendix argument, + // push an unknown object to represent that argument. (Analysis + // of dynamic call sites, especially invokehandle calls, needs + // the appendix argument on the stack, in addition to "regular" arguments + // pushed onto the stack by bytecode instructions preceding the call.) + // + // The escape analyzer does _not_ use the ciBytecodeStream::has_appendix(s) + // method to determine whether the current bytecode has an appendix argument. + // The has_appendix() method obtains the appendix from the + // ConstantPoolCacheEntry::_f1 field, which can happen concurrently with + // resolution of dynamic call sites. Callees in the + // ciBytecodeStream::get_method() call above also access the _f1 field; + // interleaving the get_method() and has_appendix() calls in the current + // method with call site resolution can lead to an inconsistent view of + // the current method's argument count. In particular, some interleaving(s) + // can cause the method's argument count to not include the appendix, which + // then leads to stack over-/underflow in the escape analyzer. + // + // Instead of pushing the argument if has_appendix() is true, the escape analyzer + // pushes an appendix for all call sites targeted by invokedynamic and invokehandle + // instructions, except if the call site is the _invokeBasic intrinsic + // (that intrinsic is always targeted by an invokehandle instruction but does + // not have an appendix argument). + if (target->is_loaded() && + Bytecodes::has_optional_appendix(s.cur_bc_raw()) && + target->intrinsic_id() != vmIntrinsics::_invokeBasic) { + state.apush(unknown_obj); + } + // Pass in raw bytecode because we need to see invokehandle instructions. + invoke(state, s.cur_bc_raw(), target, holder); + // 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()) { + state.spush(); + } else if (return_type->is_two_word()) { + state.lpush(); + } + } +#endif /* 0 */ + // qbicc does + // processReachableInstanceMethodInvoke for { virtual, interface, and special } + // processStaticElementInitialization for { static } + + break; + case Bytecodes::_new: + // TODO + // Track the class initializers for current class, supers, and interfaces with defaults + // Update set of instantiated classes + // Update set of reachable classes - walking up the heirarchy and interfaces + // Enqueue target method for processing + { + // this is a new, not a method invoke so following code not valid + // 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(); + bool will_link; + ciKlass* klass = s.get_klass(will_link); + recordReferencedType(klass); + } + break; + case Bytecodes::_newarray: + // primitive array - do nothing? Unless we need to track the primitive arrays for inclusion as well? + break; + case Bytecodes::_anewarray: + { + bool will_link; + ciKlass* arrayKlass = s.get_klass(will_link); + recordReferencedType(arrayKlass); + } + // TODO + break; + case Bytecodes::_multianewarray: + { + int i = s.cur_bcp()[3]; + bool will_link; + ciKlass* arrayKlass = s.get_klass(will_link); + // The processor of the class will need to walk the dimensions and try to load + // each dimension's class after ensuring component type is loaded. + recordReferencedType(arrayKlass); + // TODO + } + break; + case Bytecodes::_arraylength: + break; + case Bytecodes::_athrow: + fall_through = false; + break; + case Bytecodes::_checkcast: + case Bytecodes::_instanceof: + { // TODO: this is questionable - not entirely clear we should be loading classes + // eagerly here. + bool ignored_will_link; + recordReferencedType(s.get_klass(ignored_will_link)); + } + break; + case Bytecodes::_monitorenter: + case Bytecodes::_monitorexit: + break; + case Bytecodes::_wide: + ShouldNotReachHere(); + break; + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + { + int dest_bci = s.get_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + break; + } + case Bytecodes::_goto_w: + { + int dest_bci = s.get_far_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_jsr_w: + { + int dest_bci = s.get_far_dest(); + assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block"); + assert(s.next_bci() == limit_bci, "branch must end block"); + successors.push(_methodBlocks->block_containing(dest_bci)); + fall_through = false; + break; + } + case Bytecodes::_breakpoint: + break; + default: + ShouldNotReachHere(); + break; + } + + } + if (fall_through) { + int fall_through_bci = s.cur_bci(); + if (fall_through_bci < _method->code_size()) { + assert(_methodBlocks->is_block_start(fall_through_bci), "must fall through to block start."); + successors.push(_methodBlocks->block_containing(fall_through_bci)); + } + } +} + +void BCReachabilityAnalyzer::recordReferencedType(ciType *type) { + printf("ReferencedType: >"); + fflush(stdout); + type->print_name(); + printf("||Loaded=%s", type->is_loaded() ? "Y" : "N"); + printf("<\n"); + _discovered_klasses.append(type); +} + +void BCReachabilityAnalyzer::enqueueMethod(ciMethod *method) { + // Commented out as the is_loaded() call asserts + // stringStream ss; + // method->print_name(&ss); + // printf("Enqueue Method: >%s||Loaded=%s", ss.as_string(), method->is_loaded() ? "Y" : "N"); + // printf("<\n"); } \ No newline at end of file diff --git a/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp b/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp index b7e5ef020b456..b8af4a2b67713 100644 --- a/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp +++ b/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp @@ -2,12 +2,34 @@ #define SHARE_CI_BCREACHABILITYANALYZER_HPP #include "ci/ciMethod.hpp" +#include "ci/ciMethodBlocks.hpp" +#include "ci/ciType.hpp" +#include "utilities/growableArray.hpp" class BCReachabilityAnalyzer : public ResourceObj { +private: + Arena *_arena; // ciEnv arena + ciMethod *_method; + ciMethodBlocks *_methodBlocks; + + BCReachabilityAnalyzer *_parent; + + GrowableArray _discovered_klasses; + +private: + void iterate_blocks(); + void iterate_one_block(ciBlock *blk, GrowableArray &successors); + + void recordReferencedType(ciType *type); + void enqueueMethod(ciMethod *method); + + public: BCReachabilityAnalyzer(ciMethod* method, BCReachabilityAnalyzer* parent = NULL); + BCReachabilityAnalyzer* parent() { return _parent; } + ciMethod* method() const { return _method; } }; #endif //SHARE_CI_BCREACHABILITYANALYZER_HPP \ No newline at end of file From 91973f8802e8bbd3a075f9a9cf5e5a9ca157c1d7 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 16 Sep 2021 15:38:04 -0400 Subject: [PATCH 02/11] Add StaticAnalyzer Signed-off-by: Ashutosh Mehra --- .../share/compiler/abstractCompiler.hpp | 1 + .../share/compiler/compilationPolicy.cpp | 6 +++ .../share/compiler/compilationPolicy.hpp | 5 ++ src/hotspot/share/compiler/compileBroker.cpp | 52 ++++++++++++++++++- src/hotspot/share/compiler/compileBroker.hpp | 8 ++- .../share/compiler/compilerDefinitions.hpp | 8 ++- src/hotspot/share/compiler/compilerThread.cpp | 2 +- .../share/staticanalyzer/staticAnalyzer.hpp | 50 ++++++++++++++++++ 8 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 src/hotspot/share/staticanalyzer/staticAnalyzer.hpp diff --git a/src/hotspot/share/compiler/abstractCompiler.hpp b/src/hotspot/share/compiler/abstractCompiler.hpp index c3516fc2dc993..2d5dc18bd76a8 100644 --- a/src/hotspot/share/compiler/abstractCompiler.hpp +++ b/src/hotspot/share/compiler/abstractCompiler.hpp @@ -147,6 +147,7 @@ class AbstractCompiler : public CHeapObj { bool is_c1() const { return _type == compiler_c1; } bool is_c2() const { return _type == compiler_c2; } bool is_jvmci() const { return _type == compiler_jvmci; } + bool is_sa() const { return _type == static_analyzer; } CompilerType type() const { return _type; } // Customization diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 1f9902895f1f2..ed09bf34fe6a7 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -56,6 +56,7 @@ jlong CompilationPolicy::_start_time = 0; int CompilationPolicy::_c1_count = 0; int CompilationPolicy::_c2_count = 0; +int CompilationPolicy::_sa_count = 0; double CompilationPolicy::_increase_threshold_at_ratio = 0; void compilationPolicy_init() { @@ -111,6 +112,10 @@ void CompilationPolicy::compile_if_required(const methodHandle& m, TRAPS) { } } +void CompilationPolicy::analyze(const methodHandle& m, TRAPS) { + CompileBroker::compile_method(m, InvocationEntryBci, CompLevel_static_analysis, methodHandle(), 0, CompileTask::Reason_MustBeCompiled, THREAD); +} + static inline CompLevel adjust_level_for_compilability_query(CompLevel comp_level) { if (comp_level == CompLevel_any) { if (CompilerConfig::is_c1_only()) { @@ -487,6 +492,7 @@ void CompilationPolicy::initialize() { set_c1_count(MAX2(count / 3, 1)); set_c2_count(MAX2(count - c1_count(), 1)); } + set_sa_count(count); assert(count == c1_count() + c2_count(), "inconsistent compiler thread count"); set_increase_threshold_at_ratio(); } diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index 2ecb066ba8569..86ba4ebcb9250 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -166,6 +166,7 @@ class CompilationPolicy : AllStatic { static jlong _start_time; static int _c1_count, _c2_count; + static int _sa_count; static double _increase_threshold_at_ratio; // Set carry flags in the counters (in Method* and MDO). @@ -212,6 +213,7 @@ class CompilationPolicy : AllStatic { static void set_c1_count(int x) { _c1_count = x; } static void set_c2_count(int x) { _c2_count = x; } + static void set_sa_count(int x) { _sa_count = x; } enum EventType { CALL, LOOP, COMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT }; static void print_event(EventType type, const Method* m, const Method* im, int bci, CompLevel level); @@ -239,6 +241,7 @@ class CompilationPolicy : AllStatic { public: static int c1_count() { return _c1_count; } static int c2_count() { return _c2_count; } + static int sa_count() { return _sa_count; } static int compiler_count(CompLevel comp_level); // If m must_be_compiled then request a compilation from the CompileBroker. @@ -251,6 +254,8 @@ class CompilationPolicy : AllStatic { static bool can_be_osr_compiled(const methodHandle& m, int comp_level = CompLevel_any); static bool is_compilation_enabled(); + static void analyze(const methodHandle& m, TRAPS); + static void do_safepoint_work() { } static CompileTask* select_task_helper(CompileQueue* compile_queue); // Return initial compile level to use with Xcomp (depends on compilation mode). diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index bc4741b7ad944..bf1b4deff1e98 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -82,6 +82,7 @@ #ifdef COMPILER2 #include "opto/c2compiler.hpp" #endif +#include "staticanalyzer/staticAnalyzer.hpp" #ifdef DTRACE_ENABLED @@ -124,18 +125,21 @@ volatile int CompileBroker::_print_compilation_warning = 0; volatile jint CompileBroker::_should_compile_new_jobs = run_compilation; // The installed compiler(s) -AbstractCompiler* CompileBroker::_compilers[2]; +AbstractCompiler* CompileBroker::_compilers[3]; // The maximum numbers of compiler threads to be determined during startup. int CompileBroker::_c1_count = 0; int CompileBroker::_c2_count = 0; +int CompileBroker::_sa_count = 0; // An array of compiler names as Java String objects jobject* CompileBroker::_compiler1_objects = NULL; jobject* CompileBroker::_compiler2_objects = NULL; +jobject* CompileBroker::_sa_objects = NULL; CompileLog** CompileBroker::_compiler1_logs = NULL; CompileLog** CompileBroker::_compiler2_logs = NULL; +CompileLog** CompileBroker::_sa_logs = NULL; // These counters are used to assign an unique ID to each compilation. volatile jint CompileBroker::_compilation_id = 0; @@ -192,6 +196,7 @@ CompilerStatistics CompileBroker::_stats_per_level[CompLevel_full_optimization]; CompileQueue* CompileBroker::_c2_compile_queue = NULL; CompileQueue* CompileBroker::_c1_compile_queue = NULL; +CompileQueue* CompileBroker::_sa_queue = NULL; @@ -309,6 +314,10 @@ bool CompileBroker::can_remove(CompilerThread *ct, bool do_it) { if (!ReduceNumberOfCompilerThreads) return false; AbstractCompiler *compiler = ct->compiler(); + // do not allow threads for static analysis to be removed + if (compiler->is_sa()) { + return false; + } int compiler_count = compiler->num_compiler_threads(); bool c1 = compiler->is_c1(); @@ -547,6 +556,7 @@ void CompileQueue::mark_on_stack() { CompileQueue* CompileBroker::compile_queue(int comp_level) { if (is_c2_compile(comp_level)) return _c2_compile_queue; if (is_c1_compile(comp_level)) return _c1_compile_queue; + if (is_static_analysis(comp_level)) return _sa_queue; return NULL; } @@ -564,6 +574,9 @@ void CompileBroker::print_compile_queues(outputStream* st) { if (_c2_compile_queue != NULL) { _c2_compile_queue->print(st); } + if (_sa_queue != NULL) { + _sa_queue->print(st); + } } void CompileQueue::print(outputStream* st) { @@ -636,6 +649,7 @@ void CompileBroker::compilation_init_phase1(JavaThread* THREAD) { // Set the interface to the current compiler(s). _c1_count = CompilationPolicy::c1_count(); _c2_count = CompilationPolicy::c2_count(); + _sa_count = CompilationPolicy::sa_count(); #if INCLUDE_JVMCI if (EnableJVMCI) { @@ -687,6 +701,10 @@ void CompileBroker::compilation_init_phase1(JavaThread* THREAD) { } #endif // INCLUDE_JVMCI + if (_sa_count > 0) { + _compilers[2] = new StaticAnalyzer(); + } + // Start the compiler thread(s) and the sweeper thread init_compiler_sweeper_threads(); // totalTime performance counter is always created as it is required @@ -966,6 +984,11 @@ void CompileBroker::init_compiler_sweeper_threads() { _compiler1_objects = NEW_C_HEAP_ARRAY(jobject, _c1_count, mtCompiler); _compiler1_logs = NEW_C_HEAP_ARRAY(CompileLog*, _c1_count, mtCompiler); } + if (_sa_count > 0) { + _sa_queue = new CompileQueue("Static Analysis queue"); + _sa_objects = NEW_C_HEAP_ARRAY(jobject, _sa_count, mtCompiler); + _sa_logs = NEW_C_HEAP_ARRAY(CompileLog*, _sa_count, mtCompiler); + } char name_buffer[256]; @@ -1016,6 +1039,26 @@ void CompileBroker::init_compiler_sweeper_threads() { } } + for (int i = 0; i < _sa_count; i++) { + jobject thread_handle = NULL; + // Create a name for our thread. + sprintf(name_buffer, "%s Thread%d", _compilers[2]->name(), i); + Handle thread_oop = create_thread_oop(name_buffer, CHECK); + thread_handle = JNIHandles::make_global(thread_oop); + _sa_objects[i] = thread_handle; + _sa_logs[i] = NULL; + + JavaThread *ct = make_thread(compiler_t, thread_handle, _sa_queue, _compilers[2], THREAD); + assert(ct != NULL, "should have been handled for initial thread"); + _compilers[2]->set_num_compiler_threads(i + 1); + if (TraceCompilerThreads) { + ResourceMark rm; + ThreadsListHandle tlh; // name() depends on the TLH. + assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct)); + tty->print_cr("Added initial static analyzer thread %s", ct->name()); + } + } + if (UsePerfData) { PerfDataManager::create_constant(SUN_CI, "threads", PerfData::U_Bytes, _c1_count + _c2_count, CHECK); } @@ -1143,6 +1186,9 @@ void CompileBroker::mark_on_stack() { if (_c1_compile_queue != NULL) { _c1_compile_queue->mark_on_stack(); } + if (_sa_queue != NULL) { + _sa_queue->mark_on_stack(); + } } // ------------------------------------------------------------------ @@ -1829,6 +1875,10 @@ void CompileBroker::shutdown_compiler_runtime(AbstractCompiler* comp, CompilerTh _c2_compile_queue->free_all(); } + if (_sa_queue != NULL) { + _sa_queue->free_all(); + } + // Set flags so that we continue execution with using interpreter only. UseCompiler = false; UseInterpreter = true; diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp index 2159c3b76625c..c80733daf3654 100644 --- a/src/hotspot/share/compiler/compileBroker.hpp +++ b/src/hotspot/share/compiler/compileBroker.hpp @@ -160,16 +160,20 @@ class CompileBroker: AllStatic { static volatile jint _should_compile_new_jobs; // The installed compiler(s) - static AbstractCompiler* _compilers[2]; + static AbstractCompiler* _compilers[3]; // The maximum numbers of compiler threads to be determined during startup. static int _c1_count, _c2_count; + static int _sa_count; + // An array of compiler thread Java objects static jobject *_compiler1_objects, *_compiler2_objects; + static jobject *_sa_objects; // An array of compiler logs static CompileLog **_compiler1_logs, **_compiler2_logs; + static CompileLog **_sa_logs; // These counters are used for assigning id's to each compilation static volatile jint _compilation_id; @@ -178,6 +182,7 @@ class CompileBroker: AllStatic { static CompileQueue* _c2_compile_queue; static CompileQueue* _c1_compile_queue; + static CompileQueue* _sa_queue; // performance counters static PerfCounter* _perf_total_compilation; @@ -287,6 +292,7 @@ class CompileBroker: AllStatic { static AbstractCompiler* compiler(int comp_level) { if (is_c2_compile(comp_level)) return _compilers[1]; // C2 if (is_c1_compile(comp_level)) return _compilers[0]; // C1 + if (is_static_analysis(comp_level)) return _compilers[2]; // Static Analyzer return NULL; } diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp index 1c8096918a6a4..a00c6b6b75dc7 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.hpp +++ b/src/hotspot/share/compiler/compilerDefinitions.hpp @@ -36,6 +36,7 @@ enum CompilerType { compiler_c1, compiler_c2, compiler_jvmci, + static_analyzer, compiler_number_of_types }; @@ -61,7 +62,8 @@ enum CompLevel { CompLevel_simple = 1, // C1 CompLevel_limited_profile = 2, // C1, invocation & backedge counters CompLevel_full_profile = 3, // C1, invocation & backedge counters + mdo - CompLevel_full_optimization = 4 // C2 or JVMCI + CompLevel_full_optimization = 4, // C2 or JVMCI + CompLevel_static_analysis = 5 // Static Analyzer }; class CompilationModeFlag : AllStatic { @@ -100,6 +102,10 @@ inline bool is_compile(int comp_level) { return is_c1_compile(comp_level) || is_c2_compile(comp_level); } +inline bool is_static_analysis(int comp_level) { + return comp_level == CompLevel_static_analysis; +} + // States of Restricted Transactional Memory usage. enum RTMState { diff --git a/src/hotspot/share/compiler/compilerThread.cpp b/src/hotspot/share/compiler/compilerThread.cpp index 94e00083de09d..abc8ecd2e2be4 100644 --- a/src/hotspot/share/compiler/compilerThread.cpp +++ b/src/hotspot/share/compiler/compilerThread.cpp @@ -60,7 +60,7 @@ void CompilerThread::thread_entry(JavaThread* thread, TRAPS) { } bool CompilerThread::can_call_java() const { - return _compiler != NULL && _compiler->is_jvmci(); + return _compiler != NULL && (_compiler->is_jvmci() || _compiler->is_sa()); } // Create sweeper thread diff --git a/src/hotspot/share/staticanalyzer/staticAnalyzer.hpp b/src/hotspot/share/staticanalyzer/staticAnalyzer.hpp new file mode 100644 index 0000000000000..18b08a6ccda73 --- /dev/null +++ b/src/hotspot/share/staticanalyzer/staticAnalyzer.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_STATICANALYZER_STATICANALYZER_HPP +#define SHARE_STATICANALYZER_STATICANALYZER_HPP + +#include "ci/ciMethod.hpp" +#include "compiler/abstractCompiler.hpp" + +class StaticAnalyzer : public AbstractCompiler { + private: + //static bool init_analyzer_runtime(); + +public: + StaticAnalyzer() : AbstractCompiler(static_analyzer) {} + + // Name + const char *name() { return "SA"; } + void initialize(); + + // Compilation entry point for methods + void compile_method(ciEnv* env, + ciMethod* target, + int entry_bci, + bool install_code, + DirectiveSet* directive); +}; + +#endif // SHARE_STATICANALYZER_STATICANALYZER_HPP \ No newline at end of file From eb515bf4556f8bf2a98b0dfc48f24063950681d6 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Thu, 23 Sep 2021 11:16:58 -0400 Subject: [PATCH 03/11] Add VM support for static analysis Signed-off-by: Ashutosh Mehra --- src/hotspot/share/classfile/vmSymbols.hpp | 3 + src/hotspot/share/prims/nativeLookup.cpp | 2 + .../share/staticanalyzer/saJavaClasses.cpp | 94 +++++++++++++++++++ .../share/staticanalyzer/saJavaClasses.hpp | 47 ++++++++++ .../share/staticanalyzer/staticAnalyzer.cpp | 63 +++++++++++++ .../share/staticanalyzer/vmSymbols_sa.hpp | 33 +++++++ .../staticanalysis/StaticAnalyzer.java | 21 +++++ 7 files changed, 263 insertions(+) create mode 100644 src/hotspot/share/staticanalyzer/saJavaClasses.cpp create mode 100644 src/hotspot/share/staticanalyzer/saJavaClasses.hpp create mode 100644 src/hotspot/share/staticanalyzer/staticAnalyzer.cpp create mode 100644 src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp create mode 100644 src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index c7223b4b09585..75f5ce5fbe34d 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -29,6 +29,7 @@ #include "jvmci/vmSymbols_jvmci.hpp" #include "memory/iterator.hpp" #include "oops/symbol.hpp" +#include "staticanalyzer/vmSymbols_sa.hpp" #include "utilities/macros.hpp" #include "utilities/enumIterator.hpp" @@ -710,6 +711,8 @@ template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ template(url_void_signature, "(Ljava/net/URL;)V") \ \ + JVMSA_VM_SYMBOLS_DO(template) \ + \ /*end*/ // enum for figuring positions and size of Symbol::_vm_symbols[] diff --git a/src/hotspot/share/prims/nativeLookup.cpp b/src/hotspot/share/prims/nativeLookup.cpp index 16a1041e8d727..f8caa7566434f 100644 --- a/src/hotspot/share/prims/nativeLookup.cpp +++ b/src/hotspot/share/prims/nativeLookup.cpp @@ -230,6 +230,7 @@ extern "C" { jobject JNICALL JVM_GetJVMCIRuntime(JNIEnv *env, jclass c); void JNICALL JVM_RegisterJVMCINatives(JNIEnv *env, jclass compilerToVMClass); #endif + void JNICALL JVM_RegisterStaticAnalyzerNatives(JNIEnv *env, jclass saClass); } #define CC (char*) /* cast a literal from (const char*) */ @@ -254,6 +255,7 @@ static JNINativeMethod lookup_special_native_methods[] = { { CC"Java_jdk_jfr_internal_JVM_registerNatives", NULL, FN_PTR(jfr_register_natives) }, #endif { CC"Java_jdk_internal_misc_ScopedMemoryAccess_registerNatives", NULL, FN_PTR(JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods) }, + { CC"Java_jdk_internal_staticanalysis_StaticAnalyzer_registerNatives", NULL, FN_PTR(JVM_RegisterStaticAnalyzerNatives) }, }; static address lookup_special_native(const char* jni_name) { diff --git a/src/hotspot/share/staticanalyzer/saJavaClasses.cpp b/src/hotspot/share/staticanalyzer/saJavaClasses.cpp new file mode 100644 index 0000000000000..7f850f7c99909 --- /dev/null +++ b/src/hotspot/share/staticanalyzer/saJavaClasses.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" +#include "compiler/compilationPolicy.hpp" +#include "jni.h" +#include "memory/resourceArea.hpp" +#include "memory/universe.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/klass.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/jniHandles.inline.hpp" +#include "staticanalyzer/saJavaClasses.hpp" +#include "utilities/globalDefinitions.hpp" + +InstanceKlass* jdk_internal_staticanalysis_StaticAnalyzer::_klass = NULL; + +void jdk_internal_staticanalysis_StaticAnalyzer::compute_offsets(TRAPS) { + Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_staticanalysis_StaticAnalyzer(), true, CHECK); + _klass = InstanceKlass::cast(k); + _klass->initialize(CHECK); +} + +JVM_ENTRY(jboolean, SA_analyze(JNIEnv *env, jclass saClass, jlongArray handles)) + if (handles != NULL) { + typeArrayOop handles_oop = (typeArrayOop) JNIHandles::resolve(handles); + for (int i = 0; i < handles_oop->length(); i++) { + Method *method = (Method *)handles_oop->long_at(i); + methodHandle mh(thread, method); + CompilationPolicy::analyze(mh, CHECK_(JNI_FALSE)); + } + } + return JNI_TRUE; +JVM_END + +// copied from JVM_RegisterJVMCINatives +JVM_ENTRY(void, JVM_RegisterStaticAnalyzerNatives(JNIEnv *env, jclass saClass)) +{ + ResourceMark rm(thread); + HandleMark hm(thread); + ThreadToNativeFromVM trans(thread); + + // Ensure _non_oop_bits is initialized + Universe::non_oop_word(); + + if (JNI_OK != env->RegisterNatives(saClass, jdk_internal_staticanalysis_StaticAnalyzer::methods, jdk_internal_staticanalysis_StaticAnalyzer::methods_count())) { + if (!env->ExceptionCheck()) { + for (int i = 0; i < jdk_internal_staticanalysis_StaticAnalyzer::methods_count(); i++) { + JNINativeMethod *jniNativeMethod = jdk_internal_staticanalysis_StaticAnalyzer::methods + i; + if (JNI_OK != env->RegisterNatives(saClass, jniNativeMethod, 1)) { + guarantee(false, "Error registering JNI method %s%s", jniNativeMethod->name, jniNativeMethod->signature); + break; + } + } + } else { + env->ExceptionDescribe(); + } + guarantee(false, "Failed registering CompilerToVM native methods"); + } +} +JVM_END + +#define CC (char*) /*cast a literal from (const char*)*/ +#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(SA_ ## f)) + +JNINativeMethod jdk_internal_staticanalysis_StaticAnalyzer::methods[] = { + {CC "analyze", CC "([J)V", FN_PTR(analyze)}, +}; + +int jdk_internal_staticanalysis_StaticAnalyzer::methods_count() { + return sizeof(methods) / sizeof(JNINativeMethod); +} \ No newline at end of file diff --git a/src/hotspot/share/staticanalyzer/saJavaClasses.hpp b/src/hotspot/share/staticanalyzer/saJavaClasses.hpp new file mode 100644 index 0000000000000..33e5a0f6c88b0 --- /dev/null +++ b/src/hotspot/share/staticanalyzer/saJavaClasses.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_STATICANALYZER_SAJAVACLASSES_HPP +#define SHARE_STATICANALYZER_SAJAVACLASSES_HPP + +#include "classfile/vmSymbols.hpp" +#include "jni.h" +#include "oops/instanceKlass.hpp" +#include "oops/symbol.hpp" + +class jdk_internal_staticanalysis_StaticAnalyzer { + private: + static InstanceKlass* _klass; + + public: + static JNINativeMethod methods[]; + + static InstanceKlass* klass() { assert(_klass != NULL, "uninit"); return _klass; } + static Symbol* symbol() { return vmSymbols::jdk_internal_staticanalysis_StaticAnalyzer(); } + static void compute_offsets(TRAPS); + static void registerNatives(); + static int methods_count(); +}; + +#endif /* SHARE_STATICANALYZER_SAJAVACLASSES_HPP */ \ No newline at end of file diff --git a/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp b/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp new file mode 100644 index 0000000000000..5e2eaa635f0cc --- /dev/null +++ b/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "ci/bcReachabilityAnalyzer.hpp" +#include "compiler/compilerDirectives.hpp" +#include "memory/oopFactory.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "staticanalyzer/saJavaClasses.hpp" +#include "staticanalyzer/staticAnalyzer.hpp" + +void StaticAnalyzer::initialize() { + JavaThread* THREAD = JavaThread::current(); + jdk_internal_staticanalysis_StaticAnalyzer::compute_offsets(THREAD); // should this be moved to saJavaClasses.cpp +} + +void StaticAnalyzer::compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive) { + JavaThread* THREAD = JavaThread::current(); + // do reachability analysis + BCReachabilityAnalyzer *bcra = target->get_bcra(); + + // TODO: load classes discovered by bcra + + //make upcall to add callees in the set of methods to be analyzed + GrowableArray callees = bcra->get_callees(); + int numCallees = callees.length(); + if (numCallees > 0) { + typeArrayOop calleeHandles = oopFactory::new_longArray(numCallees, CHECK); + int i = 0; + for (GrowableArrayIterator it = callees.begin(); it != callees.end(); ++it) { + ciMethod *method = *it; + jlong methodId = (jlong)method->get_Method(); + calleeHandles->long_at_put(i, methodId); + i += 1; + } + JavaValue result(T_OBJECT); + JavaCallArguments args; + args.push_oop(Handle(THREAD, calleeHandles)); + JavaCalls::call_static(&result, jdk_internal_staticanalysis_StaticAnalyzer::klass(), vmSymbols::addToQueue_name(), vmSymbols::addToQueue_name_signature(), &args, THREAD); + } +} + diff --git a/src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp b/src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp new file mode 100644 index 0000000000000..b2182b7ab6397 --- /dev/null +++ b/src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_STATICANALYZER_SAVMSYMBOLS_HPP +#define SHARE_STATICANALYZER_SAVMSYMBOLS_HPP + +#define JVMSA_VM_SYMBOLS_DO(template) \ + template(jdk_internal_staticanalysis_StaticAnalyzer, "jdk/internal/staticanalysis/StaticAnalyzer") \ + template(addToQueue_name, "addToQueue") \ + template(addToQueue_name_signature, "([J)V") \ + +#endif /* SHARE_STATICANALYZER_SAVMSYMBOLS_HPP */ \ No newline at end of file diff --git a/src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java b/src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java new file mode 100644 index 0000000000000..5718b1f33128b --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java @@ -0,0 +1,21 @@ +package jdk.internal.staticanalysis; + +import java.util.Collections; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +class StaticAnalyzer { + final Set workQueue = ConcurrentHashMap.newKeySet(); + + public boolean addToQueue(long[] methodHandles) { + long[] handlesToAdd = Arrays.stream(methodHandles).filter(handle -> !workQueue.add(handle)).toArray(); + return analyze(handlesToAdd); + } + + public native boolean analyze(long[] methodHandles); + + public static native void registerNatives(); +} From 36878058c3f0a62b234859c9c24a0a016eea8292 Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Mon, 4 Oct 2021 14:22:34 -0400 Subject: [PATCH 04/11] Add get_callees() support and do the analysis in the ctor * this connects Ashu's and I's changes. Need to add the driver to test them out --- src/hotspot/share/ci/bcReachabilityAnalyzer.cpp | 10 +++++++++- src/hotspot/share/ci/bcReachabilityAnalyzer.hpp | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp b/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp index 6576bdac62887..56ed5b7032ccb 100644 --- a/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp +++ b/src/hotspot/share/ci/bcReachabilityAnalyzer.cpp @@ -12,8 +12,15 @@ BCReachabilityAnalyzer::BCReachabilityAnalyzer(ciMethod* method, BCReachabilityA , _methodBlocks(nullptr) , _parent(parent) , _discovered_klasses(_arena, 4, 0, NULL) + , _callees(_arena, 4, 0, NULL) { - // TODO + do_analysis(); +} + +void BCReachabilityAnalyzer::do_analysis() { + // identify basic blocks + _methodBlocks = _method->get_method_blocks(); + iterate_blocks(); } void BCReachabilityAnalyzer::iterate_blocks() { @@ -595,4 +602,5 @@ void BCReachabilityAnalyzer::enqueueMethod(ciMethod *method) { // method->print_name(&ss); // printf("Enqueue Method: >%s||Loaded=%s", ss.as_string(), method->is_loaded() ? "Y" : "N"); // printf("<\n"); + _callees.append(method); } \ No newline at end of file diff --git a/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp b/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp index b8af4a2b67713..e7b910c40bb11 100644 --- a/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp +++ b/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp @@ -16,6 +16,7 @@ class BCReachabilityAnalyzer : public ResourceObj { BCReachabilityAnalyzer *_parent; GrowableArray _discovered_klasses; + GrowableArray _callees; private: void iterate_blocks(); @@ -28,8 +29,15 @@ class BCReachabilityAnalyzer : public ResourceObj { public: BCReachabilityAnalyzer(ciMethod* method, BCReachabilityAnalyzer* parent = NULL); + void do_analysis(); + BCReachabilityAnalyzer* parent() { return _parent; } ciMethod* method() const { return _method; } + + GrowableArray get_callees() { + return _callees; + } + }; #endif //SHARE_CI_BCREACHABILITYANALYZER_HPP \ No newline at end of file From 0f0130985711972e62400d1d40170a872ea6647a Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Thu, 21 Oct 2021 15:59:32 -0400 Subject: [PATCH 05/11] Add getter for discovered classes --- src/hotspot/share/ci/bcReachabilityAnalyzer.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp b/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp index e7b910c40bb11..67b20639d7ec4 100644 --- a/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp +++ b/src/hotspot/share/ci/bcReachabilityAnalyzer.hpp @@ -38,6 +38,10 @@ class BCReachabilityAnalyzer : public ResourceObj { return _callees; } + GrowableArray get_classes() { + return _discovered_klasses; + } + }; #endif //SHARE_CI_BCREACHABILITYANALYZER_HPP \ No newline at end of file From da3b45df1eaae31f6288012dcb1f24ee0725000e Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Thu, 21 Oct 2021 16:01:17 -0400 Subject: [PATCH 06/11] Allow static analyzer to use c1_store (whatever that is) just like jvmci does --- src/hotspot/share/compiler/compilerDirectives.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/compiler/compilerDirectives.cpp b/src/hotspot/share/compiler/compilerDirectives.cpp index 0e38b067246ae..ae12adaefa1be 100644 --- a/src/hotspot/share/compiler/compilerDirectives.cpp +++ b/src/hotspot/share/compiler/compilerDirectives.cpp @@ -179,7 +179,7 @@ DirectiveSet* CompilerDirectives::get_for(AbstractCompiler *comp) { return _c2_store; } else { // use c1_store as default - assert(comp->is_c1() || comp->is_jvmci(), ""); + assert(comp->is_c1() || comp->is_jvmci() || comp->is_sa(), ""); return _c1_store; } } From ae1b9f1b7f9a93b941bf220e4af41b073d48b67d Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Thu, 21 Oct 2021 16:03:35 -0400 Subject: [PATCH 07/11] Support enqueuing the initial reflect method --- .../share/staticanalyzer/saJavaClasses.cpp | 22 ++++++++- .../share/staticanalyzer/vmSymbols_sa.hpp | 5 +- .../staticanalysis/StaticAnalyzer.java | 49 ++++++++++++++++--- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/staticanalyzer/saJavaClasses.cpp b/src/hotspot/share/staticanalyzer/saJavaClasses.cpp index 7f850f7c99909..5e48259cb28c6 100644 --- a/src/hotspot/share/staticanalyzer/saJavaClasses.cpp +++ b/src/hotspot/share/staticanalyzer/saJavaClasses.cpp @@ -31,6 +31,7 @@ #include "oops/instanceKlass.hpp" #include "oops/klass.hpp" #include "runtime/interfaceSupport.inline.hpp" +#include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" #include "staticanalyzer/saJavaClasses.hpp" #include "utilities/globalDefinitions.hpp" @@ -55,6 +56,24 @@ JVM_ENTRY(jboolean, SA_analyze(JNIEnv *env, jclass saClass, jlongArray handles)) return JNI_TRUE; JVM_END +JVM_ENTRY(jboolean, SA_enqueueReflectMethod(JNIEnv *env, jclass saClass, jobject method)) + if (method != NULL) { + jmethodID mid = 0; + { + ThreadToNativeFromVM ttn(thread); + mid = env->FromReflectedMethod(method); + } + Method* initialMethod = Method::checked_resolve_jmethod_id(mid); + + JavaValue result(T_VOID); + JavaCallArguments args; + args.push_long((jlong)initialMethod); + printf("Enqueueing %p\n", initialMethod); + JavaCalls::call_static(&result, jdk_internal_staticanalysis_StaticAnalyzer::klass(), vmSymbols::addToQueue_name(), vmSymbols::long_void_signature(), &args, THREAD); + } + return JNI_TRUE; +JVM_END + // copied from JVM_RegisterJVMCINatives JVM_ENTRY(void, JVM_RegisterStaticAnalyzerNatives(JNIEnv *env, jclass saClass)) { @@ -86,7 +105,8 @@ JVM_END #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(SA_ ## f)) JNINativeMethod jdk_internal_staticanalysis_StaticAnalyzer::methods[] = { - {CC "analyze", CC "([J)V", FN_PTR(analyze)}, + {CC "analyze", CC "([J)Z", FN_PTR(analyze)}, + {CC "enqueueReflectMethod", CC "(Ljava/lang/reflect/Method;)Z", FN_PTR(enqueueReflectMethod)} }; int jdk_internal_staticanalysis_StaticAnalyzer::methods_count() { diff --git a/src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp b/src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp index b2182b7ab6397..bdbb1d95b50fe 100644 --- a/src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp +++ b/src/hotspot/share/staticanalyzer/vmSymbols_sa.hpp @@ -28,6 +28,9 @@ #define JVMSA_VM_SYMBOLS_DO(template) \ template(jdk_internal_staticanalysis_StaticAnalyzer, "jdk/internal/staticanalysis/StaticAnalyzer") \ template(addToQueue_name, "addToQueue") \ - template(addToQueue_name_signature, "([J)V") \ + template(addToQueueA_signature, "([J)V") \ + template(addDiscoveredClass_name, "addDiscoveredClass") \ + template(enqueueReflectMethod_name, "enqueueReflectMethod") \ + template(enqueueReflectMethod_signature, "(Ljava/lang/reflect/Method;)V") \ #endif /* SHARE_STATICANALYZER_SAVMSYMBOLS_HPP */ \ No newline at end of file diff --git a/src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java b/src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java index 5718b1f33128b..e9c8ea92e38a2 100644 --- a/src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java +++ b/src/java.base/share/classes/jdk/internal/staticanalysis/StaticAnalyzer.java @@ -1,21 +1,58 @@ package jdk.internal.staticanalysis; +import java.lang.reflect.Method; import java.util.Collections; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentHashMap.KeySetView; import java.util.stream.Collectors; -class StaticAnalyzer { - final Set workQueue = ConcurrentHashMap.newKeySet(); +/** + * Class is static for easy up-calls from native. + */ +final class StaticAnalyzer { + static final Set workQueue = ConcurrentHashMap.newKeySet(); - public boolean addToQueue(long[] methodHandles) { - long[] handlesToAdd = Arrays.stream(methodHandles).filter(handle -> !workQueue.add(handle)).toArray(); - return analyze(handlesToAdd); + static final Set> discoveredClasses = ConcurrentHashMap.newKeySet(); + + + public static void addDiscoveredClass(Class c) { + synchronized(discoveredClasses) { + discoveredClasses.add(c); + } + } + + public static void addDiscoveredClasses(Class c[]) { + synchronized(discoveredClasses) { + discoveredClasses.addAll(Arrays.asList(c)); + } + } + + public static void addToQueue(long[] methodHandles) { + long[] handlesToAdd = Arrays.stream(methodHandles).filter(handle -> workQueue.add(handle)).toArray(); + StringBuilder sb = new StringBuilder(); + sb.append("addToQueue: ["); + Arrays.stream(handlesToAdd).mapToObj(Long::toHexString).forEach(x -> sb.append(x).append(" ")); + System.out.println(sb.toString() + "]"); + analyze(handlesToAdd); } - public native boolean analyze(long[] methodHandles); + public static void addToQueue(long methodHandle) { + long[] temp = {methodHandle}; + addToQueue(temp); + } + + public static native boolean analyze(long[] methodHandles); + + public static native boolean enqueueReflectMethod(Method m); + + public static native void registerNatives(); + + static { + registerNatives(); + } } From ffa7eb5d1c47133ee815c82a820fd64cf87dfca0 Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Thu, 21 Oct 2021 16:04:51 -0400 Subject: [PATCH 08/11] Push results of bcra back into Java Use JavaCalls to call the StaticAnalyzer java class and enqueue the found methods Similar treatment for the discovered classes Doesn't fully work yet - still dealing with assert failures due to round tripping abstract / native methods. --- .../share/staticanalyzer/staticAnalyzer.cpp | 70 ++++++++++++++----- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp b/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp index 5e2eaa635f0cc..c75a018201670 100644 --- a/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp +++ b/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp @@ -23,9 +23,11 @@ */ #include "ci/bcReachabilityAnalyzer.hpp" +#include "classfile/symbolTable.hpp" #include "compiler/compilerDirectives.hpp" #include "memory/oopFactory.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" #include "staticanalyzer/saJavaClasses.hpp" #include "staticanalyzer/staticAnalyzer.hpp" @@ -37,27 +39,63 @@ void StaticAnalyzer::initialize() { void StaticAnalyzer::compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive) { JavaThread* THREAD = JavaThread::current(); + printf("StaticAnalyzer::compile_method>>>\n"); // do reachability analysis BCReachabilityAnalyzer *bcra = target->get_bcra(); - +printf("# bcra computed\n"); // TODO: load classes discovered by bcra + { + ThreadInVMfromUnknown tiv; + ResetNoHandleMark rnhm; + Klass *accessing_klass = target->get_Method()->constants()->pool_holder(); /// TODO + Handle class_loader = Handle(THREAD, accessing_klass->class_loader()); + Handle protection_domain = Handle(THREAD, accessing_klass->protection_domain()); + + GrowableArray classes = bcra->get_classes(); + for (GrowableArrayIterator it = classes.begin(); it != classes.end(); ++it) { + ciType *c = *it; + ciKlass *k = c->as_klass(); + Klass *klazz = nullptr; + if (c->is_loaded() && false) { + // get_Klass() is protected, can't use here. Force the lookup + //klazz = k->get_Klass(); + } else { + Symbol *name_symbol = SymbolTable::new_symbol(k->name()->as_utf8(), k->name()->utf8_length()); + klazz = SystemDictionary::resolve_or_null(name_symbol, class_loader, protection_domain, THREAD); + } + if (klazz != nullptr) { + JavaValue result(T_VOID); + JavaCallArguments args; + args.push_oop(Handle(THREAD, klazz->java_mirror())); + JavaCalls::call_static(&result, jdk_internal_staticanalysis_StaticAnalyzer::klass(), vmSymbols::addDiscoveredClass_name(), vmSymbols::class_void_signature(), &args, THREAD); + } + } + - //make upcall to add callees in the set of methods to be analyzed - GrowableArray callees = bcra->get_callees(); - int numCallees = callees.length(); - if (numCallees > 0) { - typeArrayOop calleeHandles = oopFactory::new_longArray(numCallees, CHECK); - int i = 0; - for (GrowableArrayIterator it = callees.begin(); it != callees.end(); ++it) { - ciMethod *method = *it; - jlong methodId = (jlong)method->get_Method(); - calleeHandles->long_at_put(i, methodId); - i += 1; + //make upcall to add callees in the set of methods to be analyzed + GrowableArray callees = bcra->get_callees(); + int numCallees = callees.length(); + if (numCallees > 0) { + //ResetNoHandleMark rnhm; + typeArrayOop calleeHandles = oopFactory::new_longArray(numCallees, CHECK); + int i = 0; + for (GrowableArrayIterator it = callees.begin(); it != callees.end(); ++it) { + ciMethod *method = *it; + if (method->is_loaded()) { + jlong methodId = (jlong)method->get_Method(); + calleeHandles->long_at_put(i, methodId); + i += 1; + } else { + printf("# TODO deal with unloaded methods\n"); + } + } + JavaValue result(T_VOID); + JavaCallArguments args; + args.push_oop(Handle(THREAD, calleeHandles)); + printf("# calling into Java\n"); + JavaCalls::call_static(&result, jdk_internal_staticanalysis_StaticAnalyzer::klass(), vmSymbols::addToQueue_name(), vmSymbols::addToQueueA_signature(), &args, THREAD); } - JavaValue result(T_OBJECT); - JavaCallArguments args; - args.push_oop(Handle(THREAD, calleeHandles)); - JavaCalls::call_static(&result, jdk_internal_staticanalysis_StaticAnalyzer::klass(), vmSymbols::addToQueue_name(), vmSymbols::addToQueue_name_signature(), &args, THREAD); } + printf("<<< StaticAnalyzer::compile_method\n"); } From 1cdc264de700b07a6ac528faae54c6188840e698 Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Thu, 21 Oct 2021 16:06:28 -0400 Subject: [PATCH 09/11] Just enough Driver app to start the analysis of the main method --- .../jdk/internal/staticanalysis/Driver.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/java.base/share/classes/jdk/internal/staticanalysis/Driver.java diff --git a/src/java.base/share/classes/jdk/internal/staticanalysis/Driver.java b/src/java.base/share/classes/jdk/internal/staticanalysis/Driver.java new file mode 100644 index 0000000000000..e4adab30e8bdd --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/staticanalysis/Driver.java @@ -0,0 +1,61 @@ +package jdk.internal.staticanalysis; + +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.Scanner; +import java.util.Set; +import java.util.HashSet; +import jdk.internal.staticanalysis.StaticAnalyzer; + +public class Driver { + + /** + * Entrypoint for the ci-based analysis engine. + * + * args[0] = Class name of the main class + * + * @param args the args to the engine + */ + public static void main(String[] args) throws Throwable { + System.out.println("=== Analysis Driver ==="); + if (args.length < 1) { + System.out.println("Please specify the main class"); + System.exit(-1); + } + + System.out.println("Loading: ("+args[0]+")"); + Class mainClass = Class.forName(args[0], false, ClassLoader.getSystemClassLoader()); + System.out.println("Loaded: " + mainClass); + + Driver driver = new Driver(mainClass); + + Method mainMethod = driver.getStartingPoint(); + + System.out.println("===================== A ============="); + + StaticAnalyzer.enqueueReflectMethod(mainMethod); + + System.out.println("===================== B ============="); + Thread.sleep(10); + + Scanner sc = new Scanner(System.in); + sc.next(); + } + + private Class mainClass; + + + + Driver(Class aClass) { + mainClass = Objects.requireNonNull(aClass); + } + + /** + * Get the "main" method to start the analysis from + * + * @return a j.l.r.Method for mainClass.main(String[]) + */ + Method getStartingPoint() throws Throwable { + return mainClass.getDeclaredMethod("main", String[].class); + } +} \ No newline at end of file From 4c5a44a40dac5ec8722a0882840d5fdb0514c8b7 Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Thu, 21 Oct 2021 16:22:04 -0400 Subject: [PATCH 10/11] Add missing include for SystemDictionary --- src/hotspot/share/staticanalyzer/staticAnalyzer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp b/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp index c75a018201670..64560d5186482 100644 --- a/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp +++ b/src/hotspot/share/staticanalyzer/staticAnalyzer.cpp @@ -24,6 +24,7 @@ #include "ci/bcReachabilityAnalyzer.hpp" #include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" #include "compiler/compilerDirectives.hpp" #include "memory/oopFactory.hpp" #include "runtime/handles.inline.hpp" From 46efc12aff9862f3833f7c98f6c2362d7fd42621 Mon Sep 17 00:00:00 2001 From: Dan Heidinga Date: Thu, 21 Oct 2021 16:28:23 -0400 Subject: [PATCH 11/11] Update .gitignore to ignore crash files and vscode/eclipse prefs --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index 6787b23253522..3d12088de2660 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,12 @@ NashornProfile.txt /src/utils/LogCompilation/target/ /.project/ /.settings/ +hs_err_*.log +replay_pid*.log +**/.project +**/org.eclipse.core.resources.prefs +**/org.eclipse.jdt.apt.core.prefs +**/org.eclipse.jdt.core.prefs +**/org.eclipse.m2e.core.prefs +**/.factorypath +**/.classpath