/******************************************************************************* * Copyright IBM Corp. and others 2017 * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ * or the Apache License, Version 2.0 which accompanies this distribution * and is available at https://www.apache.org/licenses/LICENSE-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License, * version 2 with the GNU Classpath Exception [1] and GNU General Public * License, version 2 with the OpenJDK Assembly Exception [2]. * * [1] https://www.gnu.org/software/classpath/license.html * [2] https://openjdk.org/legal/assembly-exception.html * * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 *******************************************************************************/ #include "compile/Compilation.hpp" #include "compile/Method.hpp" #include "env/FrontEnd.hpp" #include "infra/List.hpp" #include "il/Block.hpp" #include "ilgen/VirtualMachineState.hpp" #include "ilgen/IlBuilder.hpp" #include "ilgen/MethodBuilder.hpp" #include "ilgen/BytecodeBuilder.hpp" #include "ilgen/TypeDictionary.hpp" // should really move into IlInjector.hpp #define TraceEnabled (comp()->getOption(TR_TraceILGen)) #define TraceIL(m, ...) {if (TraceEnabled) {traceMsg(comp(), m, ##__VA_ARGS__);}} OMR::BytecodeBuilder::BytecodeBuilder(TR::MethodBuilder *methodBuilder, int32_t bcIndex, const char *name, int32_t bcLength) : TR::IlBuilder(methodBuilder, methodBuilder->typeDictionary(), bcIndex), _fallThroughBuilder(0), _name(name), _bcLength(bcLength), _initialVMState(0), _vmState(0) { _successorBuilders = new (_types->trMemory()->heapMemoryRegion()) List<TR::BytecodeBuilder>(_types->trMemory()); initialize(methodBuilder->details(), methodBuilder->methodSymbol(), methodBuilder->fe(), methodBuilder->symRefTab()); initSequence(); } void TR::BytecodeBuilder::initialize(TR::IlGeneratorMethodDetails * details, TR::ResolvedMethodSymbol * methodSymbol, TR::FrontEnd * fe, TR::SymbolReferenceTable * symRefTab) { this->OMR::IlInjector::initialize(details, methodSymbol, fe, symRefTab); //addBytecodeBuilderToList relies on _comp and it won't be ready until now _methodBuilder->addToAllBytecodeBuildersList(this); } void OMR::BytecodeBuilder::appendBlock(TR::Block *block, bool addEdge) { if (block == NULL) { block = emptyBlock(); } block->setByteCodeIndex(_bcIndex, _comp); return TR::IlBuilder::appendBlock(block, addEdge); } uint32_t OMR::BytecodeBuilder::countBlocks() { // only count each block once if (_count > -1) return _count; TraceIL("[ %p ] TR::BytecodeBuilder::countBlocks 0\n", this); _count = TR::IlBuilder::countBlocks(); if (NULL != _fallThroughBuilder) _methodBuilder->addToBlockCountingWorklist(_fallThroughBuilder); ListIterator<TR::BytecodeBuilder> iter(_successorBuilders); for (TR::BytecodeBuilder *builder = iter.getFirst(); !iter.atEnd(); builder = iter.getNext()) if (builder->_count < 0) _methodBuilder->addToBlockCountingWorklist(builder); TraceIL("[ %p ] TR::BytecodeBuilder::countBlocks %d\n", this, _count); return _count; } bool OMR::BytecodeBuilder::connectTrees() { if (!_connectedTrees) { TraceIL("[ %p ] TR::BytecodeBuilder::connectTrees\n", this); bool rc = TR::IlBuilder::connectTrees(); addAllSuccessorBuildersToWorklist(); return rc; } return true; } void OMR::BytecodeBuilder::addAllSuccessorBuildersToWorklist() { if (NULL != _fallThroughBuilder) _methodBuilder->addToTreeConnectingWorklist(_fallThroughBuilder); // iterate over _successorBuilders ListIterator<TR::BytecodeBuilder> iter(_successorBuilders); for (TR::BytecodeBuilder *builder = iter.getFirst(); !iter.atEnd(); builder = iter.getNext()) _methodBuilder->addToTreeConnectingWorklist(builder); } // Must be called *after* all code has been added to the bytecode builder // Also, current VM state is assumed to be what should propagate to the fallthrough builder void OMR::BytecodeBuilder::AddFallThroughBuilder(TR::BytecodeBuilder *ftb) { TR_ASSERT_FATAL(comesBack(), "builder does not appear to have a fall through path"); TraceIL("IlBuilder[ %p ]:: fallThrough successor [ %p ]\n", this, ftb); TR::BytecodeBuilder *b = ftb; transferVMState(&b); // may change what b points at! if (b != ftb) TraceIL("IlBuilder[ %p ]:: fallThrough successor changed to [ %p ]\n", this, b); _fallThroughBuilder = b; _methodBuilder->addBytecodeBuilderToWorklist(ftb); // add explicit goto and register the actual fall-through block TR::IlBuilder *tgtb = b; TR::IlBuilder::Goto(&tgtb); } // AddSuccessorBuilders() should be called with a list of TR::BytecodeBuilder ** pointers. // Each one of these pointers could be changed by AddSuccessorBuilders() in the case where // some operations need to be inserted along the control flow edges to synchronize the // vm state from "this" builder to the target BytecodeBuilder. For this reason, the actual // control flow edges should be created (i.e. with Goto, IfCmp*, etc.) *after* calling // AddSuccessorBuilders, and the target used when creating those flow edges should take // into account that AddSuccessorBuilders may change the builder object provided. void OMR::BytecodeBuilder::AddSuccessorBuilders(uint32_t numExits, ...) { va_list exits; va_start(exits, numExits); for (auto e = 0U; e < numExits; e++) { TR::BytecodeBuilder **builder = (TR::BytecodeBuilder **) va_arg(exits, TR::BytecodeBuilder **); if ((*builder)->_bcIndex < _bcIndex) //If the successor has a bcIndex < than the current bcIndex this may be a loop _methodSymbol->setMayHaveLoops(true); transferVMState(builder); // may change what builder points at! _successorBuilders->add(*builder); // must be the bytecode builder that comes back from transferVMState() _methodBuilder->addBytecodeBuilderToWorklist(*builder); TraceIL("IlBuilder[ %p ]:: successor [ %p ]\n", this, *builder); } va_end(exits); } void OMR::BytecodeBuilder::setHandlerInfo(uint32_t catchType) { TR::Block *catchBlock = getEntry(); catchBlock->setIsCold(); catchBlock->setHandlerInfo(catchType, static_cast<uint8_t>(comp()->getInlineDepth()), -1, _methodSymbol->getResolvedMethod(), comp()); } void OMR::BytecodeBuilder::propagateVMState(TR::VirtualMachineState *fromVMState) { _initialVMState = fromVMState->makeCopy(); _vmState = fromVMState->makeCopy(); } // transferVMState needs to be called before the actual transfer operation (Goto, IfCmp, // etc.) is created because we may need to insert a builder object along that control // flow edge to synchronize the vm state at the target (in the case of a merge point). // On return, the object pointed at by the "b" parameter may have changed. The caller // should direct control for this edge to whatever the parameter passed to "b" is // pointing at on return void OMR::BytecodeBuilder::transferVMState(TR::BytecodeBuilder **b) { TR_ASSERT_FATAL(_vmState != NULL, "asked to transfer a NULL vmState from builder %p [ bci %d ]", this, _bcIndex); if ((*b)->initialVMState()) { // there is already a vm state at the target builder // so we need to synchronize this builder's vm state with the target builder's vm state // for example, the local variables holding the elements on the operand stack may not match // create an intermediate builder object to do that work TR::BytecodeBuilder *intermediateBuilder = _methodBuilder->OrphanBytecodeBuilder((*b)->_bcIndex, (*b)->_name); _vmState->mergeInto((*b)->initialVMState(), intermediateBuilder); TraceIL("IlBuilder[ %p ]:: transferVMState merged vm state on way to [ %p ] using [ %p ]\n", this, *b, intermediateBuilder); TR::IlBuilder *tgtb = *b; intermediateBuilder->IlBuilder::Goto(&tgtb); intermediateBuilder->_fallThroughBuilder = *b; TraceIL("IlBuilder[ %p ]:: fallThrough successor [ %p ]\n", intermediateBuilder, *b); *b = intermediateBuilder; // branches should direct towards intermediateBuilder not original *b } else { (*b)->propagateVMState(_vmState); } } void OMR::BytecodeBuilder::Goto(TR::BytecodeBuilder **dest) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); Goto(*dest); } void OMR::BytecodeBuilder::Goto(TR::BytecodeBuilder *dest) { AddSuccessorBuilder(&dest); OMR::IlBuilder::Goto(dest); } void OMR::BytecodeBuilder::IfCmpEqual(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpEqual(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpEqual(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpEqual(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpEqualZero(TR::BytecodeBuilder **dest, TR::IlValue *c) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpEqualZero(*dest, c); } void OMR::BytecodeBuilder::IfCmpEqualZero(TR::BytecodeBuilder *dest, TR::IlValue *c) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpEqualZero(dest, c); } void OMR::BytecodeBuilder::IfCmpNotEqual(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpNotEqual(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpNotEqual(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpNotEqual(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpNotEqualZero(TR::BytecodeBuilder **dest, TR::IlValue *c) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpNotEqualZero(*dest, c); } void OMR::BytecodeBuilder::IfCmpNotEqualZero(TR::BytecodeBuilder *dest, TR::IlValue *c) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpNotEqualZero(dest, c); } void OMR::BytecodeBuilder::IfCmpLessThan(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpLessThan(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpLessThan(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpLessThan(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedLessThan(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpUnsignedLessThan(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedLessThan(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpUnsignedLessThan(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpLessOrEqual(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpLessOrEqual(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpLessOrEqual(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpLessOrEqual(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedLessOrEqual(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpUnsignedLessOrEqual(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedLessOrEqual(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpUnsignedLessOrEqual(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpGreaterThan(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpGreaterThan(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpGreaterThan(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpGreaterThan(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedGreaterThan(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpUnsignedGreaterThan(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedGreaterThan(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpUnsignedGreaterThan(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpGreaterOrEqual(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpGreaterOrEqual(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpGreaterOrEqual(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpGreaterOrEqual(dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedGreaterOrEqual(TR::BytecodeBuilder **dest, TR::IlValue *v1, TR::IlValue *v2) { if (*dest == NULL) *dest = _methodBuilder->OrphanBytecodeBuilder(_bcIndex, _name); IfCmpUnsignedGreaterOrEqual(*dest, v1, v2); } void OMR::BytecodeBuilder::IfCmpUnsignedGreaterOrEqual(TR::BytecodeBuilder *dest, TR::IlValue *v1, TR::IlValue *v2) { TR_ASSERT_FATAL(dest != NULL, "service cannot be called with NULL destination builder"); AddSuccessorBuilder(&dest); OMR::IlBuilder::IfCmpUnsignedGreaterOrEqual(dest, v1, v2); } void * OMR::BytecodeBuilder::client() { if (_client == NULL && _clientAllocator != NULL) _client = _clientAllocator(static_cast<TR::BytecodeBuilder *>(this)); return _client; } ClientAllocator OMR::BytecodeBuilder::_clientAllocator = NULL; ClientAllocator OMR::BytecodeBuilder::_getImpl = NULL;