diff --git a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp index 256f8190b50..c2397d4a399 100644 --- a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp +++ b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -483,14 +483,14 @@ void RangeCheckEliminator::in_block_motion(BlockBegin *block, AccessIndexedList if (c) { jint value = c->type()->as_IntConstant()->value(); - if (value != min_jint) { - if (ao->op() == Bytecodes::_isub) { - value = -value; - } + if (ao->op() == Bytecodes::_iadd) { base = java_add(base, value); - last_integer = base; - last_instruction = other; + } else { + assert(ao->op() == Bytecodes::_isub, "unexpected bytecode"); + base = java_subtract(base, value); } + last_integer = base; + last_instruction = other; index = other; } else { break; diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index 1fbc49947bf..5884b2d08c6 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -344,8 +344,23 @@ Symbol* SymbolTable::lookup_common(const char* name, return sym; } +// Symbols should represent entities from the constant pool that are +// limited to <64K in length, but usage errors creep in allowing Symbols +// to be used for arbitrary strings. For debug builds we will assert if +// a string is too long, whereas product builds will truncate it. +static int check_length(const char* name, int len) { + assert(len <= Symbol::max_length(), + "String length %d exceeds the maximum Symbol length of %d", len, Symbol::max_length()); + if (len > Symbol::max_length()) { + warning("A string \"%.80s ... %.80s\" exceeds the maximum Symbol " + "length of %d and has been truncated", name, (name + len - 80), Symbol::max_length()); + len = Symbol::max_length(); + } + return len; +} + Symbol* SymbolTable::new_symbol(const char* name, int len) { - assert(len <= Symbol::max_length(), "sanity"); + len = check_length(name, len); unsigned int hash = hash_symbol(name, len, _alt_hash); Symbol* sym = lookup_common(name, len, hash); if (sym == nullptr) { @@ -485,6 +500,7 @@ void SymbolTable::new_symbols(ClassLoaderData* loader_data, const constantPoolHa for (int i = 0; i < names_count; i++) { const char *name = names[i]; int len = lengths[i]; + assert(len <= Symbol::max_length(), "must be - these come from the constant pool"); unsigned int hash = hashValues[i]; assert(lookup_shared(name, len, hash) == nullptr, "must have checked already"); Symbol* sym = do_add_if_needed(name, len, hash, is_permanent); @@ -494,6 +510,7 @@ void SymbolTable::new_symbols(ClassLoaderData* loader_data, const constantPoolHa } Symbol* SymbolTable::do_add_if_needed(const char* name, int len, uintx hash, bool is_permanent) { + assert(len <= Symbol::max_length(), "caller should have ensured this"); SymbolTableLookup lookup(name, len, hash); SymbolTableGet stg; bool clean_hint = false; @@ -542,7 +559,7 @@ Symbol* SymbolTable::do_add_if_needed(const char* name, int len, uintx hash, boo Symbol* SymbolTable::new_permanent_symbol(const char* name) { unsigned int hash = 0; - int len = (int)strlen(name); + int len = check_length(name, (int)strlen(name)); Symbol* sym = SymbolTable::lookup_only(name, len, hash); if (sym == nullptr) { sym = do_add_if_needed(name, len, hash, /* is_permanent */ true); diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index 7b60e4869e3..5e4d06f288a 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -66,9 +66,6 @@ class OopMapCacheEntry: private InterpreterOopMap { public: OopMapCacheEntry() : InterpreterOopMap() { _next = nullptr; -#ifdef ASSERT - _resource_allocate_bit_mask = false; -#endif } }; @@ -177,9 +174,13 @@ class VerifyClosure : public OffsetClosure { InterpreterOopMap::InterpreterOopMap() { initialize(); -#ifdef ASSERT - _resource_allocate_bit_mask = true; -#endif +} + +InterpreterOopMap::~InterpreterOopMap() { + if (has_valid_mask() && mask_size() > small_mask_limit) { + assert(_bit_mask[0] != 0, "should have pointer to C heap"); + FREE_C_HEAP_ARRAY(uintptr_t, _bit_mask[0]); + } } bool InterpreterOopMap::is_empty() const { @@ -399,37 +400,24 @@ void OopMapCacheEntry::deallocate(OopMapCacheEntry* const entry) { // Implementation of OopMapCache -void InterpreterOopMap::resource_copy(OopMapCacheEntry* from) { - assert(_resource_allocate_bit_mask, - "Should not resource allocate the _bit_mask"); - assert(from->has_valid_mask(), - "Cannot copy entry with an invalid mask"); +void InterpreterOopMap::copy_from(const OopMapCacheEntry* src) { + // The expectation is that this InterpreterOopMap is recently created + // and empty. It is used to get a copy of a cached entry. + assert(!has_valid_mask(), "InterpreterOopMap object can only be filled once"); + assert(src->has_valid_mask(), "Cannot copy entry with an invalid mask"); - set_method(from->method()); - set_bci(from->bci()); - set_mask_size(from->mask_size()); - set_expression_stack_size(from->expression_stack_size()); - _num_oops = from->num_oops(); + set_method(src->method()); + set_bci(src->bci()); + set_mask_size(src->mask_size()); + set_expression_stack_size(src->expression_stack_size()); + _num_oops = src->num_oops(); // Is the bit mask contained in the entry? - if (from->mask_size() <= small_mask_limit) { - memcpy((void *)_bit_mask, (void *)from->_bit_mask, - mask_word_size() * BytesPerWord); + if (src->mask_size() <= small_mask_limit) { + memcpy(_bit_mask, src->_bit_mask, mask_word_size() * BytesPerWord); } else { - // The expectation is that this InterpreterOopMap is a recently created - // and empty. It is used to get a copy of a cached entry. - // If the bit mask has a value, it should be in the - // resource area. - assert(_bit_mask[0] == 0 || - Thread::current()->resource_area()->contains((void*)_bit_mask[0]), - "The bit mask should have been allocated from a resource area"); - // Allocate the bit_mask from a Resource area for performance. Allocating - // from the C heap as is done for OopMapCache has a significant - // performance impact. - _bit_mask[0] = (uintptr_t) NEW_RESOURCE_ARRAY(uintptr_t, mask_word_size()); - assert(_bit_mask[0] != 0, "bit mask was not allocated"); - memcpy((void*) _bit_mask[0], (void*) from->_bit_mask[0], - mask_word_size() * BytesPerWord); + _bit_mask[0] = (uintptr_t) NEW_C_HEAP_ARRAY(uintptr_t, mask_word_size(), mtClass); + memcpy((void*) _bit_mask[0], (void*) src->_bit_mask[0], mask_word_size() * BytesPerWord); } } @@ -516,7 +504,7 @@ void OopMapCache::lookup(const methodHandle& method, for (int i = 0; i < _probe_depth; i++) { OopMapCacheEntry *entry = entry_at(probe + i); if (entry != nullptr && !entry->is_empty() && entry->match(method, bci)) { - entry_for->resource_copy(entry); + entry_for->copy_from(entry); assert(!entry_for->is_empty(), "A non-empty oop map should be returned"); log_debug(interpreter, oopmap)("- found at hash %d", probe + i); return; @@ -530,7 +518,7 @@ void OopMapCache::lookup(const methodHandle& method, OopMapCacheEntry* tmp = NEW_C_HEAP_OBJ(OopMapCacheEntry, mtClass); tmp->initialize(); tmp->fill(method, bci); - entry_for->resource_copy(tmp); + entry_for->copy_from(tmp); if (method->should_not_be_cached()) { // It is either not safe or not a good idea to cache this Method* @@ -631,7 +619,7 @@ void OopMapCache::compute_one_oop_map(const methodHandle& method, int bci, Inter tmp->initialize(); tmp->fill(method, bci); if (tmp->has_valid_mask()) { - entry->resource_copy(tmp); + entry->copy_from(tmp); } OopMapCacheEntry::deallocate(tmp); } diff --git a/src/hotspot/share/interpreter/oopMapCache.hpp b/src/hotspot/share/interpreter/oopMapCache.hpp index 46c85f6e879..a74073c57e9 100644 --- a/src/hotspot/share/interpreter/oopMapCache.hpp +++ b/src/hotspot/share/interpreter/oopMapCache.hpp @@ -36,13 +36,14 @@ // OopMapCache's are allocated lazily per InstanceKlass. // The oopMap (InterpreterOopMap) is stored as a bit mask. If the -// bit_mask can fit into two words it is stored in +// bit_mask can fit into four words it is stored in // the _bit_mask array, otherwise it is allocated on the heap. // For OopMapCacheEntry the bit_mask is allocated in the C heap // because these entries persist between garbage collections. -// For InterpreterOopMap the bit_mask is allocated in -// a resource area for better performance. InterpreterOopMap -// should only be created and deleted during same garbage collection. +// For InterpreterOopMap the bit_mask is allocated in the C heap +// to avoid issues with allocations from the resource area that have +// to live accross the oop closure. InterpreterOopMap should only be +// created and deleted during the same garbage collection. // // If ENABBLE_ZAP_DEAD_LOCALS is defined, two bits are used // per entry instead of one. In all cases, @@ -95,9 +96,6 @@ class InterpreterOopMap: ResourceObj { // access it without using trickery in // method bit_mask(). int _num_oops; -#ifdef ASSERT - bool _resource_allocate_bit_mask; -#endif // access methods Method* method() const { return _method; } @@ -128,12 +126,13 @@ class InterpreterOopMap: ResourceObj { public: InterpreterOopMap(); + ~InterpreterOopMap(); - // Copy the OopMapCacheEntry in parameter "from" into this - // InterpreterOopMap. If the _bit_mask[0] in "from" points to - // allocated space (i.e., the bit mask was to large to hold - // in-line), allocate the space from a Resource area. - void resource_copy(OopMapCacheEntry* from); + // Copy the OopMapCacheEntry in parameter "src" into this + // InterpreterOopMap. If the _bit_mask[0] in "src" points to + // allocated space (i.e., the bit mask was too large to hold + // in-line), allocate the space from the C heap. + void copy_from(const OopMapCacheEntry* src); void iterate_oop(OffsetClosure* oop_closure) const; void print() const; diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index 4deae372061..8fe7c2aadbf 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -54,6 +54,7 @@ uint32_t Symbol::pack_hash_and_refcount(short hash, int refcount) { } Symbol::Symbol(const u1* name, int length, int refcount) { + assert(length <= max_length(), "SymbolTable should have caught this!"); _hash_and_refcount = pack_hash_and_refcount((short)os::random(), refcount); _length = (u2)length; // _body[0..1] are allocated in the header just by coincidence in the current diff --git a/src/hotspot/share/oops/symbol.hpp b/src/hotspot/share/oops/symbol.hpp index 008b979492d..31eb3b029dd 100644 --- a/src/hotspot/share/oops/symbol.hpp +++ b/src/hotspot/share/oops/symbol.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -130,6 +130,7 @@ class Symbol : public MetaspaceObj { return (int)heap_word_size(byte_size(length)); } + // Constructor is private for use only by SymbolTable. Symbol(const u1* name, int length, int refcount); static short extract_hash(uint32_t value) { return (short)(value >> 16); } diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 9760be95ddd..6df987ad37d 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -755,6 +755,7 @@ bool IfNode::cmpi_folds(PhaseIterGVN* igvn, bool fold_ne) { bool IfNode::is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn) { return ctrl != nullptr && ctrl->is_Proj() && + ctrl->outcnt() == 1 && // No side-effects ctrl->in(0) != nullptr && ctrl->in(0)->Opcode() == Op_If && ctrl->in(0)->outcnt() == 2 && @@ -1328,7 +1329,7 @@ Node* IfNode::fold_compares(PhaseIterGVN* igvn) { if (cmpi_folds(igvn)) { Node* ctrl = in(0); - if (is_ctrl_folds(ctrl, igvn) && ctrl->outcnt() == 1) { + if (is_ctrl_folds(ctrl, igvn)) { // A integer comparison immediately dominated by another integer // comparison ProjNode* success = nullptr; diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index 677e1bcd1d1..a548aea5610 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -870,6 +870,10 @@ class MachSafePointNode : public MachReturnNode { assert(verify_jvms(jvms), "jvms must match"); return in(_jvmadj + jvms->monitor_box_offset(idx)); } + Node* scalarized_obj(const JVMState* jvms, uint idx) const { + assert(verify_jvms(jvms), "jvms must match"); + return in(_jvmadj + jvms->scloff() + idx); + } void set_local(const JVMState* jvms, uint idx, Node *c) { assert(verify_jvms(jvms), "jvms must match"); set_req(_jvmadj + jvms->locoff() + idx, c); diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 0ad56184d4d..60d1ca57e56 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -974,6 +974,27 @@ bool PhaseOutput::contains_as_owner(GrowableArray *monarray, Obje return false; } +// Determine if there is a scalar replaced object description represented by 'ov'. +bool PhaseOutput::contains_as_scalarized_obj(JVMState* jvms, MachSafePointNode* sfn, + GrowableArray* objs, + ObjectValue* ov) const { + for (int i = 0; i < jvms->scl_size(); i++) { + Node* n = sfn->scalarized_obj(jvms, i); + // Other kinds of nodes that we may encounter here, for instance constants + // representing values of fields of objects scalarized, aren't relevant for + // us, since they don't map to ObjectValue. + if (!n->is_SafePointScalarObject()) { + continue; + } + + ObjectValue* other = (ObjectValue*) sv_for_node_id(objs, n->_idx); + if (ov == other) { + return true; + } + } + return false; +} + //--------------------------Process_OopMap_Node-------------------------------- void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { // Handle special safepoint nodes for synchronization @@ -1137,7 +1158,10 @@ void PhaseOutput::Process_OopMap_Node(MachNode *mach, int current_offset) { for (int j = 0; j< merge->possible_objects()->length(); j++) { ObjectValue* ov = merge->possible_objects()->at(j)->as_ObjectValue(); - bool is_root = locarray->contains(ov) || exparray->contains(ov) || contains_as_owner(monarray, ov); + bool is_root = locarray->contains(ov) || + exparray->contains(ov) || + contains_as_owner(monarray, ov) || + contains_as_scalarized_obj(jvms, sfn, objs, ov); ov->set_root(is_root); } } diff --git a/src/hotspot/share/opto/output.hpp b/src/hotspot/share/opto/output.hpp index 17179553602..fbef7c99fb5 100644 --- a/src/hotspot/share/opto/output.hpp +++ b/src/hotspot/share/opto/output.hpp @@ -209,6 +209,9 @@ class PhaseOutput : public Phase { bool starts_bundle(const Node *n) const; bool contains_as_owner(GrowableArray *monarray, ObjectValue *ov) const; + bool contains_as_scalarized_obj(JVMState* jvms, MachSafePointNode* sfn, + GrowableArray* objs, + ObjectValue* ov) const; // Dump formatted assembly #if defined(SUPPORT_OPTO_ASSEMBLY) diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 0b940a13c54..0655f4c1e65 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -3717,27 +3717,55 @@ void SuperWord::adjust_pre_loop_limit_to_align_main_loop_vectors() { TRACE_ALIGN_VECTOR_NODE(mask_AW); TRACE_ALIGN_VECTOR_NODE(adjust_pre_iter); - // 4: Compute (3a, b): + // 4: The computation of the new pre-loop limit could overflow (for 3a) or + // underflow (for 3b) the int range. This is problematic in combination + // with Range Check Elimination (RCE), which determines a "safe" range + // where a RangeCheck will always succeed. RCE adjusts the pre-loop limit + // such that we only enter the main-loop once we have reached the "safe" + // range, and adjusts the main-loop limit so that we exit the main-loop + // before we leave the "safe" range. After RCE, the range of the main-loop + // can only be safely narrowed, and should never be widened. Hence, the + // pre-loop limit can only be increased (for stride > 0), but an add + // overflow might decrease it, or decreased (for stride < 0), but a sub + // underflow might increase it. To prevent that, we perform the Sub / Add + // and Max / Min with long operations. + old_limit = new ConvI2LNode(old_limit); + orig_limit = new ConvI2LNode(orig_limit); + adjust_pre_iter = new ConvI2LNode(adjust_pre_iter); + phase()->register_new_node(old_limit, pre_ctrl); + phase()->register_new_node(orig_limit, pre_ctrl); + phase()->register_new_node(adjust_pre_iter, pre_ctrl); + TRACE_ALIGN_VECTOR_NODE(old_limit); + TRACE_ALIGN_VECTOR_NODE(orig_limit); + TRACE_ALIGN_VECTOR_NODE(adjust_pre_iter); + + // 5: Compute (3a, b): // new_limit = old_limit + adjust_pre_iter (stride > 0) // new_limit = old_limit - adjust_pre_iter (stride < 0) + // Node* new_limit = nullptr; if (stride < 0) { - new_limit = new SubINode(old_limit, adjust_pre_iter); + new_limit = new SubLNode(old_limit, adjust_pre_iter); } else { - new_limit = new AddINode(old_limit, adjust_pre_iter); + new_limit = new AddLNode(old_limit, adjust_pre_iter); } phase()->register_new_node(new_limit, pre_ctrl); TRACE_ALIGN_VECTOR_NODE(new_limit); - // 5: Compute (15a, b): + // 6: Compute (15a, b): // Prevent pre-loop from going past the original limit of the loop. Node* constrained_limit = - (stride > 0) ? (Node*) new MinINode(new_limit, orig_limit) - : (Node*) new MaxINode(new_limit, orig_limit); + (stride > 0) ? (Node*) new MinLNode(phase()->C, new_limit, orig_limit) + : (Node*) new MaxLNode(phase()->C, new_limit, orig_limit); + phase()->register_new_node(constrained_limit, pre_ctrl); + TRACE_ALIGN_VECTOR_NODE(constrained_limit); + + // 7: We know that the result is in the int range, there is never truncation + constrained_limit = new ConvL2INode(constrained_limit); phase()->register_new_node(constrained_limit, pre_ctrl); TRACE_ALIGN_VECTOR_NODE(constrained_limit); - // 6: Hack the pre-loop limit + // 8: Hack the pre-loop limit igvn().replace_input_of(pre_opaq, 1, constrained_limit); } diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 8f5d2ad4acb..1aed46d5880 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -947,7 +947,6 @@ void frame::oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool quer InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f); // process locals & expression stack - ResourceMark rm(thread); InterpreterOopMap mask; if (query_oop_map_cache) { m->mask_for(m, bci, &mask); diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index a99c36e7eb6..157c895a050 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -44,6 +44,9 @@ #include "utilities/events.hpp" #include "utilities/exceptions.hpp" +// Limit exception message components to 64K (the same max as Symbols) +#define MAX_LEN 65535 + // Implementation of ThreadShadow void check_ThreadShadow() { const ByteSize offset1 = byte_offset_of(ThreadShadow, _pending_exception); @@ -116,10 +119,11 @@ bool Exceptions::special_exception(JavaThread* thread, const char* file, int lin const char* exc_value = h_exception.not_null() ? h_exception->print_value_string() : h_name != nullptr ? h_name->as_C_string() : "null"; - log_info(exceptions)("Thread cannot call Java so instead of throwing exception <%s%s%s> (" PTR_FORMAT ") \n" + log_info(exceptions)("Thread cannot call Java so instead of throwing exception <%.*s%s%.*s> (" PTR_FORMAT ") \n" "at [%s, line %d]\nfor thread " PTR_FORMAT ",\n" "throwing pre-allocated exception: %s", - exc_value, message ? ": " : "", message ? message : "", + MAX_LEN, exc_value, message ? ": " : "", + MAX_LEN, message ? message : "", p2i(h_exception()), file, line, p2i(thread), Universe::vm_exception()->print_value_string()); // We do not care what kind of exception we get for a thread which @@ -145,10 +149,11 @@ void Exceptions::_throw(JavaThread* thread, const char* file, int line, Handle h // tracing (do this up front - so it works during boot strapping) // Note, the print_value_string() argument is not called unless logging is enabled! - log_info(exceptions)("Exception <%s%s%s> (" PTR_FORMAT ") \n" + log_info(exceptions)("Exception <%.*s%s%.*s> (" PTR_FORMAT ") \n" "thrown [%s, line %d]\nfor thread " PTR_FORMAT, - h_exception->print_value_string(), - message ? ": " : "", message ? message : "", + MAX_LEN, h_exception->print_value_string(), + message ? ": " : "", + MAX_LEN, message ? message : "", p2i(h_exception()), file, line, p2i(thread)); // for AbortVMOnException flag @@ -568,13 +573,13 @@ void Exceptions::log_exception(Handle exception, const char* message) { ResourceMark rm; const char* detail_message = java_lang_Throwable::message_as_utf8(exception()); if (detail_message != nullptr) { - log_info(exceptions)("Exception <%s: %s>\n thrown in %s", - exception->print_value_string(), - detail_message, - message); + log_info(exceptions)("Exception <%.*s: %.*s>\n thrown in %.*s", + MAX_LEN, exception->print_value_string(), + MAX_LEN, detail_message, + MAX_LEN, message); } else { - log_info(exceptions)("Exception <%s>\n thrown in %s", - exception->print_value_string(), - message); + log_info(exceptions)("Exception <%.*s>\n thrown in %.*s", + MAX_LEN, exception->print_value_string(), + MAX_LEN, message); } } diff --git a/src/hotspot/share/utilities/utf8.cpp b/src/hotspot/share/utilities/utf8.cpp index d7798778b2c..6fd877120df 100644 --- a/src/hotspot/share/utilities/utf8.cpp +++ b/src/hotspot/share/utilities/utf8.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "memory/allocation.hpp" +#include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/utf8.hpp" @@ -431,12 +432,16 @@ int UNICODE::utf8_size(jbyte c) { template int UNICODE::utf8_length(const T* base, int length) { - int result = 0; + size_t result = 0; for (int index = 0; index < length; index++) { T c = base[index]; - result += utf8_size(c); + int sz = utf8_size(c); + if (result + sz > INT_MAX-1) { + break; + } + result += sz; } - return result; + return checked_cast(result); } template diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 86666b1f894..c27e4c6ebe0 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, 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 @@ -2150,27 +2150,33 @@ private void doCopyArea(int x, int y, int w, int h, int dx, int dy) { } Blit ob = lastCAblit; - if (dy == 0 && dx > 0 && dx < w) { - while (w > 0) { - int partW = Math.min(w, dx); - w -= partW; - int sx = x + w; - ob.Blit(theData, theData, comp, clip, - sx, y, sx+dx, y+dy, partW, h); + try { + if (dy == 0 && dx > 0 && dx < w) { + while (w > 0) { + int partW = Math.min(w, dx); + w -= partW; + int sx = Math.addExact(x, w); + ob.Blit(theData, theData, comp, clip, + sx, y, sx+dx, y+dy, partW, h); + } + return; } - return; - } - if (dy > 0 && dy < h && dx > -w && dx < w) { - while (h > 0) { - int partH = Math.min(h, dy); - h -= partH; - int sy = y + h; - ob.Blit(theData, theData, comp, clip, - x, sy, x+dx, sy+dy, w, partH); + if (dy > 0 && dy < h && dx > -w && dx < w) { + while (h > 0) { + int partH = Math.min(h, dy); + h -= partH; + int sy = Math.addExact(y, h); + ob.Blit(theData, theData, comp, clip, + x, sy, Math.addExact(x, dx), sy+dy, w, partH); + } + return; } + ob.Blit(theData, theData, comp, clip, x, y, + Math.addExact(x, dx), Math.addExact(y, dy), w, h); + } catch (ArithmeticException ex) { + // We are hitting integer overflow in Math.addExact() return; } - ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h); } /* diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java b/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java index 7275a01ce06..00563a84ecf 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, 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 @@ -369,6 +369,13 @@ protected void renderImageXform(SunGraphics2D sg, Image img, final AffineTransform itx; try { itx = tx.createInverse(); + double[] mat = new double[6]; + itx.getMatrix(mat); + for (double d : mat) { + if (!Double.isFinite(d)) { + return; + } + } } catch (final NoninvertibleTransformException ignored) { // Non-invertible transform means no output return; diff --git a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h index c4eae3c19aa..8975f8d4a9d 100644 --- a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h +++ b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, 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 @@ -32,6 +32,7 @@ #define _Included_SurfaceData #include +#include #ifdef __cplusplus extern "C" { @@ -53,6 +54,14 @@ typedef struct { #define SD_RASINFO_PRIVATE_SIZE 64 +#define UNSAFE_TO_ADD(a, b) \ + (((a >= 0) && (b >= 0) && (a > (INT_MAX - b))) || \ + ((a < 0) && (b < 0) && (a < (INT_MIN - b)))) \ + +#define UNSAFE_TO_SUB(a, b) \ + (((b >= 0) && (a < 0) && (a < (INT_MIN + b))) || \ + ((b < 0) && (a >= 0) && (-b > (INT_MAX - a)))) \ + /* * The SurfaceDataRasInfo structure is used to pass in and return various * pieces of information about the destination drawable. In particular: diff --git a/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c b/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c index 21b716e3bcd..e8c8765dd2c 100644 --- a/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c +++ b/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, 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 @@ -68,14 +68,28 @@ Java_sun_java2d_loops_MaskBlit_MaskBlit return; } + if (width <= 0 || height <= 0) { + return; + } + srcInfo.bounds.x1 = srcx; srcInfo.bounds.y1 = srcy; + if (UNSAFE_TO_ADD(srcx, width) || + UNSAFE_TO_ADD(srcy, height) || + UNSAFE_TO_ADD(dstx, width) || + UNSAFE_TO_ADD(dsty, height)) { + return; + } srcInfo.bounds.x2 = srcx + width; srcInfo.bounds.y2 = srcy + height; dstInfo.bounds.x1 = dstx; dstInfo.bounds.y1 = dsty; dstInfo.bounds.x2 = dstx + width; dstInfo.bounds.y2 = dsty + height; + if (UNSAFE_TO_SUB(srcx, dstx) || + UNSAFE_TO_SUB(srcy, dsty)) { + return; + } srcx -= dstx; srcy -= dsty; SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds); diff --git a/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c b/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c index 354934069c0..fe0bc406860 100644 --- a/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c +++ b/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, 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 @@ -467,7 +467,7 @@ storePgram(EdgeInfo *pLeftEdge, EdgeInfo *pRightEdge, #define INSERT_ACCUM(pACCUM, IMIN, IMAX, X0, Y0, X1, Y1, CX1, CX2, MULT) \ do { \ jdouble xmid = ((X0) + (X1)) * 0.5; \ - if (xmid <= (CX2)) { \ + if (xmid < (CX2)) { \ jdouble sliceh = ((Y1) - (Y0)); \ jdouble slicearea; \ jint i; \ @@ -556,7 +556,7 @@ fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, jint cy2 = pRasInfo->bounds.y2; jint width = cx2 - cx1; EdgeInfo edges[4]; - jfloat localaccum[MASK_BUF_LEN + 1]; + jfloat localaccum[MASK_BUF_LEN + 2]; jfloat *pAccum; if (!storePgram(edges + 0, edges + 2, @@ -568,12 +568,12 @@ fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, } pAccum = ((width > MASK_BUF_LEN) - ? malloc((width + 1) * sizeof(jfloat)) + ? malloc((width + 2) * sizeof(jfloat)) : localaccum); if (pAccum == NULL) { return; } - memset(pAccum, 0, (width+1) * sizeof(jfloat)); + memset(pAccum, 0, (width + 2) * sizeof(jfloat)); while (cy1 < cy2) { jint lmin, lmax, rmin, rmax; @@ -794,7 +794,7 @@ drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, jint cy2 = pRasInfo->bounds.y2; jint width = cx2 - cx1; EdgeInfo edges[8]; - jfloat localaccum[MASK_BUF_LEN + 1]; + jfloat localaccum[MASK_BUF_LEN + 2]; jfloat *pAccum; if (!storePgram(edges + 0, edges + 6, @@ -815,12 +815,12 @@ drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, JNI_TRUE); pAccum = ((width > MASK_BUF_LEN) - ? malloc((width + 1) * sizeof(jfloat)) + ? malloc((width + 2) * sizeof(jfloat)) : localaccum); if (pAccum == NULL) { return; } - memset(pAccum, 0, (width+1) * sizeof(jfloat)); + memset(pAccum, 0, (width + 2) * sizeof(jfloat)); while (cy1 < cy2) { jint lmin, lmax, rmin, rmax; diff --git a/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c b/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c index 4d7442d7aef..02c99ea9ada 100644 --- a/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c +++ b/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, 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 @@ -120,7 +120,7 @@ TransformInterpFunc *pBicubicFunc = BicubicInterp; /* We reject coordinates not less than 1<<30 so that the distance between */ /* any 2 of them is less than 1<<31 which would overflow into the sign */ /* bit of a signed long value used to represent fixed point coordinates. */ -#define TX_FIXED_UNSAFE(v) (fabs(v) >= (1<<30)) +#define TX_FIXED_UNSAFE(v) (isinf(v) || isnan(v) || fabs(v) >= (1<<30)) static jboolean checkOverflow(jint dxoff, jint dyoff, SurfaceDataBounds *pBounds, diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNestedScalarized.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNestedScalarized.java new file mode 100644 index 00000000000..5916490a02c --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNestedScalarized.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, 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. + */ + +/* + * @test + * @bug 8331194 + * @summary Check that Reduce Allocation Merges doesn't crash when an input + * of the Phi is not the _current_ output of the Phi but said input + * needs to be rematerialized because it's used regardless of the + * Phi output. + * @run main/othervm -XX:CompileCommand=dontinline,*TestReduceAllocationAndNestedScalarized*::test + * -XX:CompileCommand=compileonly,*TestReduceAllocationAndNestedScalarized*::test + * -XX:CompileCommand=compileonly,*Picture*::*init* + * -XX:CompileCommand=compileonly,*Point*::*init* + * -XX:CompileCommand=exclude,*Unloaded*::* + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation + * -XX:-UseCompressedOops + * -Xcomp + * -server + * compiler.escapeAnalysis.TestReduceAllocationAndNestedScalarized + * @run main compiler.escapeAnalysis.TestReduceAllocationAndNestedScalarized + */ + +package compiler.escapeAnalysis; + +public class TestReduceAllocationAndNestedScalarized { + static class Picture { + public Point first; + public Point second; + } + + static class Point { + int x; + } + + static class Unloaded { + } + + static int test(boolean cond) { + Picture p = new Picture(); + p.first = new Point(); + Point p2 = p.first; + + if (cond) p2 = new Point(); + + p.second = p2; + + new Unloaded(); + + return p.first.x; + } + + public static void main(String[] args) { + Picture pic = new Picture(); + Point pnt = new Point(); + int res = test(true); + System.out.println("Result is: " + res); + } +} diff --git a/test/jdk/java/nio/channels/DatagramChannel/Loopback.java b/test/jdk/java/nio/channels/DatagramChannel/Loopback.java index a61a4d0b177..5562378b83f 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/Loopback.java +++ b/test/jdk/java/nio/channels/DatagramChannel/Loopback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, 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 @@ -118,7 +118,8 @@ static void test(ProtocolFamily family, InetAddress group, NetworkInterface ni) // send datagram to multicast group System.out.format("send %s -> %s%n", dc.getLocalAddress(), target); - ByteBuffer src = ByteBuffer.wrap("hello".getBytes("UTF-8")); + String str = "hello " + System.nanoTime(); + ByteBuffer src = ByteBuffer.wrap(str.getBytes("UTF-8")); dc.send(src, target); // receive datagram sent to multicast group @@ -142,6 +143,7 @@ static void test(ProtocolFamily family, InetAddress group, NetworkInterface ni) System.out.format("send %s -> %s%n", dc.getLocalAddress(), target); src.clear(); dc.send(src, target); + src.flip(); // test that we don't receive the datagram sent to multicast group dc.configureBlocking(false); @@ -157,10 +159,16 @@ static void test(ProtocolFamily family, InetAddress group, NetworkInterface ni) } else { sel.selectedKeys().clear(); SocketAddress sender = dc.receive(dst); + if (src.mismatch(dst) != -1) { + System.out.println("src: " + src + "not equal to dst: " + dst); + dst.clear(); + continue; + } if (sender != null) { System.out.format("received %s from %s%n", dst, sender); senderPort = ((InetSocketAddress) sender).getPort(); - assertTrue(senderPort != localPort, "Unexpected message"); + assertTrue(senderPort != localPort, + "Unexpected message: localPort=" + localPort); } } }