From 1257df7331bbf5f99ecbcccc0e2b1de0726960ea Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 19 Mar 2014 00:10:01 +0700 Subject: [PATCH 001/290] Adds const operator[] to TByteObject Issue: #32 --- include/types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/types.h b/include/types.h index f8791da..909f726 100644 --- a/include/types.h +++ b/include/types.h @@ -182,6 +182,7 @@ struct TByteObject : public TObject { const uint8_t* getBytes() const { return bytes; } const uint8_t& getByte(uint32_t index) const { return bytes[index]; } uint8_t& operator [] (uint32_t index) { return bytes[index]; } + const uint8_t& operator [] (uint32_t index) const { return bytes[index]; } void putByte(uint32_t index, uint8_t value) { bytes[index] = value; } From 24127fcf3a659dd87badb9fd453a4a88da4ca8f1 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 16 Mar 2014 21:45:09 +0700 Subject: [PATCH 002/290] Adds base types for Smalltlak instruction API Commit provides basic data structures for operating Smalltalk bytecodes in a friendly way. Base concept is stolen drom the LLVM and other software which deals with basic blocks and so on. Issue: #32 --- CMakeLists.txt | 2 + include/instructions.h | 143 ++++++++++++++++++++++++++++++++++ src/TSmalltalkInstruction.cpp | 57 ++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 include/instructions.h create mode 100644 src/TSmalltalkInstruction.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cfa42ea..2cb9aae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,8 @@ set(CPP_FILES src/TInstruction.cpp src/TSymbol.cpp src/vm.cpp + + src/TSmalltalkInstruction.cpp ) if (LLVM) diff --git a/include/instructions.h b/include/instructions.h new file mode 100644 index 0000000..9d62979 --- /dev/null +++ b/include/instructions.h @@ -0,0 +1,143 @@ +#ifndef LLST_INSTRUCTIONS_INCLUDED +#define LLST_INSTRUCTIONS_INCLUDED + +#include +#include +#include +#include +#include + +#include +#include + +namespace st { + +struct TSmalltalkInstruction { + typedef opcode::Opcode TOpcode; + typedef uint8_t TArgument; + typedef uint16_t TExtra; + typedef uint32_t TUnpackedBytecode; + + TSmalltalkInstruction(TOpcode opcode, TArgument argument = 0, TExtra extra = 0) + : m_opcode(opcode), m_argument(argument), m_extra(extra) {} + + // Initialize instruction from the unpacked value + TSmalltalkInstruction(TUnpackedBytecode bytecode) { + m_opcode = static_cast(bytecode & 0xFF); + m_argument = static_cast((bytecode >> 8) & 0xFF); + m_extra = static_cast((bytecode >> 16) & 0xFF); + } + + // Decode instruction from method bytecode + // Shifts bytePointer to the next instruction + TSmalltalkInstruction(const TByteObject& byteCodes, uint16_t& bytePointer); + + TOpcode getOpcode() const { return m_opcode; } + TArgument getArgument() const { return m_argument; } + TExtra getExtra() const { return m_extra; } + + // Return fixed width representation of bytecode suitable for storing in arrays + TUnpackedBytecode serialize() const { + return static_cast(m_opcode) | (m_argument << 8) | (m_extra << 16); + } + +private: + TOpcode m_opcode; + TArgument m_argument; + TExtra m_extra; +}; + +class BasicBlock { +public: + typedef std::vector TInstructionVector; + + class iterator : public TInstructionVector::iterator { + public: + iterator(const TInstructionVector::iterator& copy) : TInstructionVector::iterator(copy) { } + + const TSmalltalkInstruction operator *() const { + return TSmalltalkInstruction(TInstructionVector::iterator::operator*()); + } + + TInstructionVector::iterator& get() { return static_cast(*this); } + const TInstructionVector::iterator& get() const { return static_cast(*this); } + }; + + iterator begin() { return iterator(m_instructions.begin()); } + iterator end() { return iterator(m_instructions.end()); } + + // Append instruction to the end of basic block + void append(TSmalltalkInstruction instruction) { + m_instructions.push_back(instruction.serialize()); + } + + // Insert instruction at specified position + void insert(const iterator& position, TSmalltalkInstruction instruction) { + m_instructions.insert(position, instruction.serialize()); + } + + // Replace existing instruction at specified position with the new one + void replace(const iterator& position, TSmalltalkInstruction instruction) { + assert(position != m_instructions.end()); + + const TInstructionVector::iterator replacePosition = position; + *replacePosition = instruction.serialize(); + } + + // Remove instruction from basic block + void remove(const iterator& position) { + assert(position != m_instructions.end()); + m_instructions.erase(position); + } + + // Split current basic block at specified position + // Current block will hold instructions prior to the cut position + // Returned block will hold the rest + BasicBlock* split(const iterator& position) { + BasicBlock* newBlock = new BasicBlock; + std::copy(position.get(), m_instructions.end(), newBlock->m_instructions.begin()); + m_instructions.erase(position, m_instructions.end()); + // TODO insert jump instruction and add newBlock to the parsed method + return newBlock; + } + +private: + TInstructionVector m_instructions; +}; + +class ParsedMethod { +public: + typedef std::list TBasicBlockList; + typedef TBasicBlockList::iterator iterator; + iterator begin() { return m_basicBlocks.begin(); } + iterator end() { return m_basicBlocks.end(); } + + BasicBlock* createBasicBlock() { + m_basicBlocks.push_back(new BasicBlock); + return m_basicBlocks.back(); + } + + ParsedMethod(TMethod* method); + ParsedMethod() {} + + ~ParsedMethod() { + for (TBasicBlockList::iterator iBlock = m_basicBlocks.begin(), + end = m_basicBlocks.end(); iBlock != end; ++iBlock) + { + delete * iBlock; + } + } + +private: + void parse(TMethod* method); + +private: + TBasicBlockList m_basicBlocks; + + typedef std::map TOffsetToBasicBlockMap; + TOffsetToBasicBlockMap m_offsetToBasicBlock; +}; + +} // namespace st + +#endif diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp new file mode 100644 index 0000000..a3bb89f --- /dev/null +++ b/src/TSmalltalkInstruction.cpp @@ -0,0 +1,57 @@ +#include + +using namespace st; + +TSmalltalkInstruction::TSmalltalkInstruction(const TByteObject& byteCodes, uint16_t& bytePointer) + : m_opcode(opcode::extended), m_argument(0), m_extra(0) +{ + const uint8_t& bytecode = byteCodes[bytePointer++]; + + // For normal bytecodes higher part of the byte holds opcode + // whether lower part holds the argument + m_opcode = static_cast(bytecode >> 4); + m_argument = bytecode & 0x0F; + + // Extended opcodes encode argument in a separate byte + // Opcode is stored in a lower half of the first byte + if (m_opcode == opcode::extended) { + m_opcode = static_cast(m_argument); + m_argument = byteCodes[bytePointer++]; + } + + // Some instructions hold extra data in a bytes right after instruction + switch (m_opcode) { + case opcode::pushBlock: + // Storing bytecode offset as extra + m_extra = byteCodes[bytePointer] | (byteCodes[bytePointer+1] << 8); + bytePointer += 2; + break; + + case opcode::doPrimitive: + // Primitive number do not fit into lower 4 bits of opcode byte. + // So it is stored in a separate byte right after. Technically, + // this value is an argument for instruction so it would be logical + // to hold it in the argument field. + m_argument = byteCodes[bytePointer++]; + break; + + case opcode::doSpecial: + switch (m_argument) { + case special::branch: + case special::branchIfTrue: + case special::branchIfFalse: + // Storing jump target offset as extra + m_extra = byteCodes[bytePointer] | (byteCodes[bytePointer+1] << 8); + bytePointer += 2; + break; + + case special::sendToSuper: + m_extra = byteCodes[bytePointer++]; + break; + } + break; + + default: // Nothing to do here + break; + } +} From cb4f0172826ec3db670bcef11c6a328d3178146a Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 18 Mar 2014 00:27:13 +0700 Subject: [PATCH 003/290] Adds ParsedMethod::parse() Parse method provides a way for translating compiled Smalltalk method into intermediate form convinient for analysis and modification. After method is parsed, one may traverse it using basic block or instruction iterators. Issue: #32 --- CMakeLists.txt | 1 + src/ParsedMethod.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/ParsedMethod.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cb9aae..e71c3d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ set(CPP_FILES src/vm.cpp src/TSmalltalkInstruction.cpp + src/ParsedMethod.cpp ) if (LLVM) diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp new file mode 100644 index 0000000..99e05df --- /dev/null +++ b/src/ParsedMethod.cpp @@ -0,0 +1,69 @@ +#include + +using namespace st; + +void ParsedMethod::parse(TMethod* method) { + assert(method); + assert(method->byteCodes); + + uint16_t bytePointer = 0; + + TByteObject& byteCodes = * method->byteCodes; + const uint16_t stopPointer = byteCodes.getSize(); + + // Scaning the method's bytecodes for branch sites and collecting branch targets. + // Creating target basic blocks beforehand and collecting them in a map. + while (bytePointer < stopPointer) { + // Decoding instruction and shifting byte pointer to the next one + TSmalltalkInstruction instruction(byteCodes, bytePointer); + + if (instruction.getOpcode() == opcode::pushBlock) { + // Skipping the nested block's bytecodes + // Extra holds the new bytecode offset + bytePointer = instruction.getExtra(); + continue; + } + + // We're now looking only for branch bytecodes + if (instruction.getOpcode() != opcode::doSpecial) + continue; + + switch (instruction.getArgument()) { + case special::branch: + case special::branchIfTrue: + case special::branchIfFalse: { + const uint16_t targetOffset = instruction.getExtra(); + + // Checking whether current branch target is already known + if (m_offsetToBasicBlock.find(targetOffset) == m_offsetToBasicBlock.end()) { + // Creating the referred basic block and inserting it into the function + // Later it will be filled with instructions and linked to other blocks + BasicBlock* targetBasicBlock = createBasicBlock(); + m_offsetToBasicBlock[targetOffset] = targetBasicBlock; + } + } break; + } + } + + // Populating previously created basic blocks with actual instructions + BasicBlock* currentBasicBlock = m_offsetToBasicBlock[0]; + + // If no branch site points to 0 offset then we create block ourselves + if (! currentBasicBlock) { + m_offsetToBasicBlock[0] = currentBasicBlock = new BasicBlock; + + // Pushing block from the beginning to comply it's position + m_basicBlocks.push_front(currentBasicBlock); + } + + while (bytePointer < stopPointer) { + // Switching basic block if current offset is a branch target + const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(bytePointer); + if (iBlock != m_offsetToBasicBlock.end()) + currentBasicBlock = iBlock->second; + + // Fetching instruction and appending it to the current basic block + TSmalltalkInstruction instruction(byteCodes, bytePointer); + currentBasicBlock->append(instruction); + } +} From 2c79d82ed806db13b21df2b9dee31192bb305611 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 22 Mar 2014 11:46:36 +0700 Subject: [PATCH 004/290] Adds block and instruction visitors Issue: #32 --- include/instructions.h | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/include/instructions.h b/include/instructions.h index 9d62979..fdaeccb 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -138,6 +138,50 @@ class ParsedMethod { TOffsetToBasicBlockMap m_offsetToBasicBlock; }; +class BasicBlockVisitor { +public: + BasicBlockVisitor(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } + virtual ~BasicBlockVisitor() { } + + virtual bool visitBlock(BasicBlock& basicBlock) { return true; } + + void run() { + ParsedMethod::iterator iBlock = m_parsedMethod->begin(); + const ParsedMethod::iterator iEnd = m_parsedMethod->end(); + + while (iBlock != iEnd) { + if (! visitBlock(** iBlock)) + break; + + ++iBlock; + } + } + +private: + ParsedMethod* m_parsedMethod; +}; + +class InstructionVisitor : public BasicBlockVisitor { +public: + InstructionVisitor(ParsedMethod* parsedMethod) : BasicBlockVisitor(parsedMethod) { } + virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { return true; } + +protected: + virtual bool visitBlock(BasicBlock& basicBlock) { + BasicBlock::iterator iInstruction = basicBlock.begin(); + const BasicBlock::iterator iEnd = basicBlock.end(); + + while (iInstruction != iEnd) { + if (! visitInstruction(* iInstruction)) + return false; + + ++iInstruction; + } + + return true; + } +}; + } // namespace st #endif From 816a7b81084159acb2020a74a55c285c97e05ea4 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 22 Mar 2014 12:31:08 +0700 Subject: [PATCH 005/290] Adds block offset field Issue: #32 --- include/instructions.h | 8 ++++++-- src/ParsedMethod.cpp | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index fdaeccb..3e18e39 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -101,7 +101,11 @@ class BasicBlock { return newBlock; } + uint16_t getOffset() const { return m_offset; } + + BasicBlock(uint16_t blockOffset = 0) : m_offset(blockOffset) { } private: + uint16_t m_offset; TInstructionVector m_instructions; }; @@ -112,8 +116,8 @@ class ParsedMethod { iterator begin() { return m_basicBlocks.begin(); } iterator end() { return m_basicBlocks.end(); } - BasicBlock* createBasicBlock() { - m_basicBlocks.push_back(new BasicBlock); + BasicBlock* createBasicBlock(uint16_t blockOffset = 0) { + m_basicBlocks.push_back(new BasicBlock(blockOffset)); return m_basicBlocks.back(); } diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 99e05df..9835c34 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -38,7 +38,7 @@ void ParsedMethod::parse(TMethod* method) { if (m_offsetToBasicBlock.find(targetOffset) == m_offsetToBasicBlock.end()) { // Creating the referred basic block and inserting it into the function // Later it will be filled with instructions and linked to other blocks - BasicBlock* targetBasicBlock = createBasicBlock(); + BasicBlock* targetBasicBlock = createBasicBlock(bytePointer); m_offsetToBasicBlock[targetOffset] = targetBasicBlock; } } break; @@ -50,7 +50,7 @@ void ParsedMethod::parse(TMethod* method) { // If no branch site points to 0 offset then we create block ourselves if (! currentBasicBlock) { - m_offsetToBasicBlock[0] = currentBasicBlock = new BasicBlock; + m_offsetToBasicBlock[0] = currentBasicBlock = new BasicBlock(0); // Pushing block from the beginning to comply it's position m_basicBlocks.push_front(currentBasicBlock); From fd765e934d5c1efe774040cc954bbf0e07261810 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 23 Mar 2014 15:44:18 +0700 Subject: [PATCH 006/290] Adds ParsedBlock class and actual parsing code Issue: #32 --- CMakeLists.txt | 1 + include/instructions.h | 57 +++++++++++++++++++++++++++++++++++++++--- src/ParsedBlock.cpp | 10 ++++++++ src/ParsedMethod.cpp | 38 ++++++++++++++++++++-------- 4 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 src/ParsedBlock.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e71c3d5..df8fd3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ set(CPP_FILES src/TSmalltalkInstruction.cpp src/ParsedMethod.cpp + src/ParsedBlock.cpp ) if (LLVM) diff --git a/include/instructions.h b/include/instructions.h index 3e18e39..e78e27d 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -101,6 +101,7 @@ class BasicBlock { return newBlock; } + // Offset of first instruction of this basic block within the method's bytecodes uint16_t getOffset() const { return m_offset; } BasicBlock(uint16_t blockOffset = 0) : m_offset(blockOffset) { } @@ -109,7 +110,9 @@ class BasicBlock { TInstructionVector m_instructions; }; +class ParsedBlock; class ParsedMethod { + friend class ParsedBlock; public: typedef std::list TBasicBlockList; typedef TBasicBlockList::iterator iterator; @@ -121,8 +124,13 @@ class ParsedMethod { return m_basicBlocks.back(); } - ParsedMethod(TMethod* method); ParsedMethod() {} + ParsedMethod(TMethod* method) : m_origin(method) { + assert(method); + parse(); + } + + TMethod* getOrigin() const { return m_origin; } ~ParsedMethod() { for (TBasicBlockList::iterator iBlock = m_basicBlocks.begin(), @@ -132,14 +140,55 @@ class ParsedMethod { } } -private: - void parse(TMethod* method); +protected: + // Protected constructor for ParsedBlock + ParsedMethod(TMethod* method, bool parseBytecodes = true) + : m_origin(method) + { + assert(method); + if (parseBytecodes) + parse(); + } -private: + void parse(uint16_t startOffset = 0, uint16_t stopOffset = 0); + virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); + void addParsedBlock(uint16_t offset, ParsedBlock* parsedBlock) { + m_offsetToParsedBlock[offset] = parsedBlock; + } + + TMethod* m_origin; TBasicBlockList m_basicBlocks; typedef std::map TOffsetToBasicBlockMap; TOffsetToBasicBlockMap m_offsetToBasicBlock; + + typedef std::map TOffsetToParsedBlockMap; + TOffsetToParsedBlockMap m_offsetToParsedBlock; +}; + +class ParsedBlock : public ParsedMethod { +public: + ParsedBlock(ParsedMethod* parsedMethod, uint16_t startOffset, uint16_t stopOffset) + : ParsedMethod(parsedMethod->getOrigin(), false), m_startOffset(startOffset), m_stopOffset(stopOffset) + { + parse(startOffset, stopOffset); + } + + // Parsed method encapsulating the block's bytecodes + ParsedMethod* getContainer() const { return m_containerMethod; } + + // First instruction offset within method's bytecodes + uint16_t getStartOffset() const { return m_startOffset; } + + // Instruction offset after the last block's instruction + uint16_t getStopOffset() const { return m_stopOffset; } + +protected: + virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); + + ParsedMethod* m_containerMethod; + uint16_t m_startOffset; + uint16_t m_stopOffset; }; class BasicBlockVisitor { diff --git a/src/ParsedBlock.cpp b/src/ParsedBlock.cpp new file mode 100644 index 0000000..cc3f1d2 --- /dev/null +++ b/src/ParsedBlock.cpp @@ -0,0 +1,10 @@ +#include + +using namespace st; + +void ParsedBlock::parseBlock(uint16_t startOffset, uint16_t stopOffset) { + ParsedMethod* container = getContainer(); + + ParsedBlock* nestedBlock = new ParsedBlock(container, startOffset, stopOffset); + container->addParsedBlock(startOffset, nestedBlock); +} diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 9835c34..012b4df 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -2,14 +2,20 @@ using namespace st; -void ParsedMethod::parse(TMethod* method) { - assert(method); - assert(method->byteCodes); +void ParsedMethod::parseBlock(uint16_t startOffset, uint16_t stopOffset) { + // Following instruction belong to the nested code block + // ParsedBlock will decode all of it's instructions and nested blocks + ParsedBlock* parsedBlock = new ParsedBlock(this, startOffset, stopOffset); + m_offsetToParsedBlock[startOffset] = parsedBlock; +} + +void ParsedMethod::parse(uint16_t startOffset, uint16_t stopOffset) { + assert(m_origin && m_origin->byteCodes); - uint16_t bytePointer = 0; + uint16_t bytePointer = startOffset; - TByteObject& byteCodes = * method->byteCodes; - const uint16_t stopPointer = byteCodes.getSize(); + TByteObject& byteCodes = * m_origin->byteCodes; + const uint16_t stopPointer = stopOffset ? stopOffset : byteCodes.getSize(); // Scaning the method's bytecodes for branch sites and collecting branch targets. // Creating target basic blocks beforehand and collecting them in a map. @@ -18,9 +24,19 @@ void ParsedMethod::parse(TMethod* method) { TSmalltalkInstruction instruction(byteCodes, bytePointer); if (instruction.getOpcode() == opcode::pushBlock) { + // Preserving the start block's offset + const uint16_t startOffset = bytePointer; + // Extra holds the bytecode offset right after the block + const uint16_t stopOffset = instruction.getExtra(); + + // Parsing block. This operation depends on + // whether we're in a method or in a block. + // Nested blocks are registered in the + // container method, not the outer block. + parseBlock(startOffset, stopOffset); + // Skipping the nested block's bytecodes - // Extra holds the new bytecode offset - bytePointer = instruction.getExtra(); + bytePointer = stopOffset; continue; } @@ -46,11 +62,11 @@ void ParsedMethod::parse(TMethod* method) { } // Populating previously created basic blocks with actual instructions - BasicBlock* currentBasicBlock = m_offsetToBasicBlock[0]; + BasicBlock* currentBasicBlock = m_offsetToBasicBlock[startOffset]; - // If no branch site points to 0 offset then we create block ourselves + // If no branch site points to start offset then we create block ourselves if (! currentBasicBlock) { - m_offsetToBasicBlock[0] = currentBasicBlock = new BasicBlock(0); + m_offsetToBasicBlock[startOffset] = currentBasicBlock = new BasicBlock(startOffset); // Pushing block from the beginning to comply it's position m_basicBlocks.push_front(currentBasicBlock); From c53e3360d5517c279b88962daa4f08ade5918ef3 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 23 Mar 2014 18:03:28 +0700 Subject: [PATCH 007/290] Extracts common interface out of ParsedMethod and ParsedBlock Issue: #32 --- CMakeLists.txt | 1 + include/instructions.h | 73 ++++++++++++++++++++++----------------- src/ParsedBytecode.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++ src/ParsedMethod.cpp | 75 ---------------------------------------- 4 files changed, 120 insertions(+), 107 deletions(-) create mode 100644 src/ParsedBytecode.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index df8fd3a..036af24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ set(CPP_FILES src/TSmalltalkInstruction.cpp src/ParsedMethod.cpp + src/ParsedBytecode.cpp src/ParsedBlock.cpp ) diff --git a/include/instructions.h b/include/instructions.h index e78e27d..6ae9a79 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -110,9 +110,10 @@ class BasicBlock { TInstructionVector m_instructions; }; -class ParsedBlock; -class ParsedMethod { - friend class ParsedBlock; +// This is a base class for ParsedMethod and ParsedBlock +// Provides common interface for iterating basic blocks and +// instructions which is used by corresponding visitors +class ParsedBytecode { public: typedef std::list TBasicBlockList; typedef TBasicBlockList::iterator iterator; @@ -124,15 +125,7 @@ class ParsedMethod { return m_basicBlocks.back(); } - ParsedMethod() {} - ParsedMethod(TMethod* method) : m_origin(method) { - assert(method); - parse(); - } - - TMethod* getOrigin() const { return m_origin; } - - ~ParsedMethod() { + virtual ~ParsedBytecode() { for (TBasicBlockList::iterator iBlock = m_basicBlocks.begin(), end = m_basicBlocks.end(); iBlock != end; ++iBlock) { @@ -140,36 +133,51 @@ class ParsedMethod { } } -protected: - // Protected constructor for ParsedBlock - ParsedMethod(TMethod* method, bool parseBytecodes = true) - : m_origin(method) - { - assert(method); - if (parseBytecodes) - parse(); - } + // Returns actual method object which was parsed + TMethod* getOrigin() const { return m_origin; } +protected: + ParsedBytecode(TMethod* method) : m_origin(method) { } void parse(uint16_t startOffset = 0, uint16_t stopOffset = 0); - virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); - void addParsedBlock(uint16_t offset, ParsedBlock* parsedBlock) { - m_offsetToParsedBlock[offset] = parsedBlock; - } + // Descendants should override this method to provide block handling + virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset) = 0; + +protected: TMethod* m_origin; TBasicBlockList m_basicBlocks; typedef std::map TOffsetToBasicBlockMap; TOffsetToBasicBlockMap m_offsetToBasicBlock; +}; + +class ParsedBlock; +class ParsedMethod : public ParsedBytecode { + friend class ParsedBlock; // for addParsedBlock() + +public: + ParsedMethod(TMethod* method) : ParsedBytecode(method) { + assert(method); + parse(); + } +protected: + virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); + + void addParsedBlock(uint16_t offset, ParsedBlock* parsedBlock) { + m_offsetToParsedBlock[offset] = parsedBlock; + } + +protected: typedef std::map TOffsetToParsedBlockMap; TOffsetToParsedBlockMap m_offsetToParsedBlock; }; -class ParsedBlock : public ParsedMethod { +class ParsedBlock : public ParsedBytecode { public: ParsedBlock(ParsedMethod* parsedMethod, uint16_t startOffset, uint16_t stopOffset) - : ParsedMethod(parsedMethod->getOrigin(), false), m_startOffset(startOffset), m_stopOffset(stopOffset) + : ParsedBytecode(parsedMethod->getOrigin()), m_containerMethod(parsedMethod), + m_startOffset(startOffset), m_stopOffset(stopOffset) { parse(startOffset, stopOffset); } @@ -186,6 +194,7 @@ class ParsedBlock : public ParsedMethod { protected: virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); +protected: ParsedMethod* m_containerMethod; uint16_t m_startOffset; uint16_t m_stopOffset; @@ -193,14 +202,14 @@ class ParsedBlock : public ParsedMethod { class BasicBlockVisitor { public: - BasicBlockVisitor(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } + BasicBlockVisitor(ParsedBytecode* parsedBytecode) : m_parsedBytecode(parsedBytecode) { } virtual ~BasicBlockVisitor() { } virtual bool visitBlock(BasicBlock& basicBlock) { return true; } void run() { - ParsedMethod::iterator iBlock = m_parsedMethod->begin(); - const ParsedMethod::iterator iEnd = m_parsedMethod->end(); + ParsedBytecode::iterator iBlock = m_parsedBytecode->begin(); + const ParsedBytecode::iterator iEnd = m_parsedBytecode->end(); while (iBlock != iEnd) { if (! visitBlock(** iBlock)) @@ -211,12 +220,12 @@ class BasicBlockVisitor { } private: - ParsedMethod* m_parsedMethod; + ParsedBytecode* m_parsedBytecode; }; class InstructionVisitor : public BasicBlockVisitor { public: - InstructionVisitor(ParsedMethod* parsedMethod) : BasicBlockVisitor(parsedMethod) { } + InstructionVisitor(ParsedBytecode* parsedBytecode) : BasicBlockVisitor(parsedBytecode) { } virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { return true; } protected: diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp new file mode 100644 index 0000000..be92281 --- /dev/null +++ b/src/ParsedBytecode.cpp @@ -0,0 +1,78 @@ +#include + +using namespace st; + +void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { + assert(m_origin && m_origin->byteCodes); + + uint16_t bytePointer = startOffset; + + TByteObject& byteCodes = * m_origin->byteCodes; + const uint16_t stopPointer = stopOffset ? stopOffset : byteCodes.getSize(); + + // Scaning the method's bytecodes for branch sites and collecting branch targets. + // Creating target basic blocks beforehand and collecting them in a map. + while (bytePointer < stopPointer) { + // Decoding instruction and shifting byte pointer to the next one + TSmalltalkInstruction instruction(byteCodes, bytePointer); + + if (instruction.getOpcode() == opcode::pushBlock) { + // Preserving the start block's offset + const uint16_t startOffset = bytePointer; + // Extra holds the bytecode offset right after the block + const uint16_t stopOffset = instruction.getExtra(); + + // Parsing block. This operation depends on + // whether we're in a method or in a block. + // Nested blocks are registered in the + // container method, not the outer block. + parseBlock(startOffset, stopOffset); + + // Skipping the nested block's bytecodes + bytePointer = stopOffset; + continue; + } + + // We're now looking only for branch bytecodes + if (instruction.getOpcode() != opcode::doSpecial) + continue; + + switch (instruction.getArgument()) { + case special::branch: + case special::branchIfTrue: + case special::branchIfFalse: { + const uint16_t targetOffset = instruction.getExtra(); + + // Checking whether current branch target is already known + if (m_offsetToBasicBlock.find(targetOffset) == m_offsetToBasicBlock.end()) { + // Creating the referred basic block and inserting it into the function + // Later it will be filled with instructions and linked to other blocks + BasicBlock* targetBasicBlock = createBasicBlock(bytePointer); + m_offsetToBasicBlock[targetOffset] = targetBasicBlock; + } + } break; + } + } + + // Populating previously created basic blocks with actual instructions + BasicBlock* currentBasicBlock = m_offsetToBasicBlock[startOffset]; + + // If no branch site points to start offset then we create block ourselves + if (! currentBasicBlock) { + m_offsetToBasicBlock[startOffset] = currentBasicBlock = new BasicBlock(startOffset); + + // Pushing block from the beginning to comply it's position + m_basicBlocks.push_front(currentBasicBlock); + } + + while (bytePointer < stopPointer) { + // Switching basic block if current offset is a branch target + const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(bytePointer); + if (iBlock != m_offsetToBasicBlock.end()) + currentBasicBlock = iBlock->second; + + // Fetching instruction and appending it to the current basic block + TSmalltalkInstruction instruction(byteCodes, bytePointer); + currentBasicBlock->append(instruction); + } +} diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 012b4df..4b064d3 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -8,78 +8,3 @@ void ParsedMethod::parseBlock(uint16_t startOffset, uint16_t stopOffset) { ParsedBlock* parsedBlock = new ParsedBlock(this, startOffset, stopOffset); m_offsetToParsedBlock[startOffset] = parsedBlock; } - -void ParsedMethod::parse(uint16_t startOffset, uint16_t stopOffset) { - assert(m_origin && m_origin->byteCodes); - - uint16_t bytePointer = startOffset; - - TByteObject& byteCodes = * m_origin->byteCodes; - const uint16_t stopPointer = stopOffset ? stopOffset : byteCodes.getSize(); - - // Scaning the method's bytecodes for branch sites and collecting branch targets. - // Creating target basic blocks beforehand and collecting them in a map. - while (bytePointer < stopPointer) { - // Decoding instruction and shifting byte pointer to the next one - TSmalltalkInstruction instruction(byteCodes, bytePointer); - - if (instruction.getOpcode() == opcode::pushBlock) { - // Preserving the start block's offset - const uint16_t startOffset = bytePointer; - // Extra holds the bytecode offset right after the block - const uint16_t stopOffset = instruction.getExtra(); - - // Parsing block. This operation depends on - // whether we're in a method or in a block. - // Nested blocks are registered in the - // container method, not the outer block. - parseBlock(startOffset, stopOffset); - - // Skipping the nested block's bytecodes - bytePointer = stopOffset; - continue; - } - - // We're now looking only for branch bytecodes - if (instruction.getOpcode() != opcode::doSpecial) - continue; - - switch (instruction.getArgument()) { - case special::branch: - case special::branchIfTrue: - case special::branchIfFalse: { - const uint16_t targetOffset = instruction.getExtra(); - - // Checking whether current branch target is already known - if (m_offsetToBasicBlock.find(targetOffset) == m_offsetToBasicBlock.end()) { - // Creating the referred basic block and inserting it into the function - // Later it will be filled with instructions and linked to other blocks - BasicBlock* targetBasicBlock = createBasicBlock(bytePointer); - m_offsetToBasicBlock[targetOffset] = targetBasicBlock; - } - } break; - } - } - - // Populating previously created basic blocks with actual instructions - BasicBlock* currentBasicBlock = m_offsetToBasicBlock[startOffset]; - - // If no branch site points to start offset then we create block ourselves - if (! currentBasicBlock) { - m_offsetToBasicBlock[startOffset] = currentBasicBlock = new BasicBlock(startOffset); - - // Pushing block from the beginning to comply it's position - m_basicBlocks.push_front(currentBasicBlock); - } - - while (bytePointer < stopPointer) { - // Switching basic block if current offset is a branch target - const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(bytePointer); - if (iBlock != m_offsetToBasicBlock.end()) - currentBasicBlock = iBlock->second; - - // Fetching instruction and appending it to the current basic block - TSmalltalkInstruction instruction(byteCodes, bytePointer); - currentBasicBlock->append(instruction); - } -} From 1f18e97350b73c2f3124e093243614a0dd6732c1 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 23 Mar 2014 19:49:40 +0700 Subject: [PATCH 008/290] Adds ParsedMethod::block_iterator to traverse nested parsed blocks Issue: #32 --- include/instructions.h | 29 ++++++++++++++++++++++++++++- src/ParsedMethod.cpp | 8 ++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/instructions.h b/include/instructions.h index 6ae9a79..45c633d 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -136,6 +136,14 @@ class ParsedBytecode { // Returns actual method object which was parsed TMethod* getOrigin() const { return m_origin; } + BasicBlock* getBasicBlockByOffset(uint16_t offset) { + TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(offset); + if (iBlock != m_offsetToBasicBlock.end()) + return iBlock->second; + else + return 0; + } + protected: ParsedBytecode(TMethod* method) : m_origin(method) { } void parse(uint16_t startOffset = 0, uint16_t stopOffset = 0); @@ -156,19 +164,38 @@ class ParsedMethod : public ParsedBytecode { friend class ParsedBlock; // for addParsedBlock() public: + typedef std::list TParsedBlockList; + ParsedMethod(TMethod* method) : ParsedBytecode(method) { assert(method); parse(); } + virtual ~ParsedMethod(); + + typedef TParsedBlockList::iterator block_iterator; + block_iterator blockBegin() { return m_parsedBlocks.begin(); } + block_iterator blockEnd() { return m_parsedBlocks.end(); } + + ParsedBlock* getParsedBlockByOffset(uint16_t startOffset) { + TOffsetToParsedBlockMap::iterator iBlock = m_offsetToParsedBlock.find(startOffset); + if (iBlock != m_offsetToParsedBlock.end()) + return iBlock->second; + else + return 0; + } + protected: virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); void addParsedBlock(uint16_t offset, ParsedBlock* parsedBlock) { + m_parsedBlocks.push_back(parsedBlock); m_offsetToParsedBlock[offset] = parsedBlock; } protected: + TParsedBlockList m_parsedBlocks; + typedef std::map TOffsetToParsedBlockMap; TOffsetToParsedBlockMap m_offsetToParsedBlock; }; @@ -219,7 +246,7 @@ class BasicBlockVisitor { } } -private: +protected: ParsedBytecode* m_parsedBytecode; }; diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 4b064d3..4d5a210 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -8,3 +8,11 @@ void ParsedMethod::parseBlock(uint16_t startOffset, uint16_t stopOffset) { ParsedBlock* parsedBlock = new ParsedBlock(this, startOffset, stopOffset); m_offsetToParsedBlock[startOffset] = parsedBlock; } + +ParsedMethod::~ParsedMethod() { + for (TParsedBlockList::iterator iBlock = m_parsedBlocks.begin(), + end = m_parsedBlocks.end(); iBlock != end; ++iBlock) + { + delete * iBlock; + } +} From 3f99ee1e57b07e6bdc78fcf5728087513da55b7c Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 23 Mar 2014 21:46:11 +0700 Subject: [PATCH 009/290] Adds visitor for traversing parsed blocks Issue: #32 --- include/instructions.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/instructions.h b/include/instructions.h index 45c633d..4ef9741 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -271,6 +271,30 @@ class InstructionVisitor : public BasicBlockVisitor { } }; +class ParsedBlockVisitor { +public: + ParsedBlockVisitor(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } + virtual ~ParsedBlockVisitor() { } + + void run() { + ParsedMethod::block_iterator iBlock = m_parsedMethod->blockBegin(); + const ParsedMethod::block_iterator iEnd = m_parsedMethod->blockEnd(); + + while (iBlock != iEnd) { + if (! visitBlock(** iBlock)) + break; + + ++iBlock; + } + } + +protected: + virtual bool visitBlock(ParsedBlock& parsedBlock) { return true; } + +private: + ParsedMethod* m_parsedMethod; +}; + } // namespace st #endif From 1d6bb2fef79244f3787b30489562c478e31a2231 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 22 Mar 2014 21:31:58 +0700 Subject: [PATCH 010/290] Adds block return detector Issue: #32 --- include/analysis.h | 63 ++++++++++++++++++++++++++++++++++++++++++ include/instructions.h | 4 +++ 2 files changed, 67 insertions(+) create mode 100644 include/analysis.h diff --git a/include/analysis.h b/include/analysis.h new file mode 100644 index 0000000..5f5b972 --- /dev/null +++ b/include/analysis.h @@ -0,0 +1,63 @@ +#ifndef LLST_ANALYSIS_INCLUDED +#define LLST_ANALYSIS_INCLUDED + +#include + +namespace st { + +// This detector scans method's nested blocks for block return instruction. +// Only block's instructions are traversed. Method's instructions are skipped. +// +// This pass is used to find out whether method code contains block return instruction. +// This instruction is handled in a very different way than the usual opcodes. +// Thus requires special handling. Block return is done by trowing an exception out of +// the block containing it. Then it's catched by the method's code to perform a return. +// In order not to bloat the code with unused try-catch code we're previously scanning +// the method's code to ensure that try-catch is really needed. If it is not, we simply +// skip its generation. +class BlockReturnDetector : public ParsedBlockVisitor { +public: + BlockReturnDetector(ParsedMethod* parsedMethod) : ParsedBlockVisitor(parsedMethod), m_blockReturnFound(false) { } + + bool isBlockReturnFound() const { return m_blockReturnFound; } + +protected: + class InstructionDetector : public InstructionVisitor { + public: + InstructionDetector(ParsedBytecode* parsedBytecode) : InstructionVisitor(parsedBytecode), m_blockReturnFound(false) { } + bool isBlockReturnFound() const { return m_blockReturnFound; } + + private: + virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { + if (instruction.getOpcode() == opcode::doSpecial) { + if (instruction.getArgument() == special::blockReturn) { + m_blockReturnFound = true; + return false; + } + } + + return true; + } + + bool m_blockReturnFound; + }; + + virtual bool visitBlock(ParsedBlock& parsedBlock) { + InstructionDetector detector(&parsedBlock); + detector.run(); + + if (detector.isBlockReturnFound()) { + m_blockReturnFound = true; + return false; + } + + return true; + } + +private: + bool m_blockReturnFound; +}; + +} // namespace st + +#endif diff --git a/include/instructions.h b/include/instructions.h index 4ef9741..599e7df 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -66,6 +66,10 @@ class BasicBlock { iterator begin() { return iterator(m_instructions.begin()); } iterator end() { return iterator(m_instructions.end()); } + const TSmalltalkInstruction operator[](const std::size_t index) const { + return TSmalltalkInstruction(m_instructions[index]); + } + // Append instruction to the end of basic block void append(TSmalltalkInstruction instruction) { m_instructions.push_back(instruction.serialize()); From c7c3a4e21c08cc3af0f4ffcb5589e9f0cf58fe25 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 30 Mar 2014 22:55:22 +0700 Subject: [PATCH 011/290] Adds control graph API ControlGraph represents method's instruction as a graph where each instruction is linked to others in specific order which reflects control and data flow. Later this information will be used for type inference. Issue: #32 --- CMakeLists.txt | 2 + include/analysis.h | 187 +++++++++++++++++++++++++++++++++++++++++++ src/ControlGraph.cpp | 7 ++ 3 files changed, 196 insertions(+) create mode 100644 src/ControlGraph.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 036af24..5f86cf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,8 @@ set(CPP_FILES src/ParsedMethod.cpp src/ParsedBytecode.cpp src/ParsedBlock.cpp + + src/ControlGraph.cpp ) if (LLVM) diff --git a/include/analysis.h b/include/analysis.h index 5f5b972..f22bfb3 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -1,6 +1,8 @@ #ifndef LLST_ANALYSIS_INCLUDED #define LLST_ANALYSIS_INCLUDED +#include + #include namespace st { @@ -58,6 +60,191 @@ class BlockReturnDetector : public ParsedBlockVisitor { bool m_blockReturnFound; }; +class ControlDomain; +class ControlNode; +typedef std::set TNodeSet; +typedef std::set TDomainSet; + +// ControlNode is a base class for elements of ControlGraph. +// Elements of a graph represent various types of relations +// of code, data and metainfo. Each node is linked to other +// nodes of a graph. Nodes within a graph are organized in +// groups which are called domains. +class ControlNode { +public: + enum TNodeType { + ntInstruction, // node representing VM instruction + ntPhi, // virtual node linking stack values from different blocks + ntTau // virtual node linkinig variable types from assignment sites + }; + + virtual ~ControlNode() { } + virtual TNodeType getNodeType() const = 0; + + ControlDomain* getDomain() const { return m_domain; } + void setDomain(ControlDomain* value) { m_domain = value; } + + TNodeSet& getInEdges() { return m_inEdges; } + TNodeSet& getOutEdges() { return m_outEdges; } + + void addEdge(ControlNode* to) { + m_outEdges.insert(to); + to->getInEdges().insert(this); + } + + void removeEdge(ControlNode* to) { + m_outEdges.erase(to); + to->getInEdges().erase(this); + } +private: + TNodeType m_type; + TNodeSet m_inEdges; + TNodeSet m_outEdges; + + ControlDomain* m_domain; +}; + +// Instruction node represents a signle VM instruction and it's relations in code. +class InstructionNode : public ControlNode { +public: + InstructionNode() : m_instruction(opcode::extended) { } + virtual TNodeType getNodeType() const { return ntInstruction; } + + void setInstruction(TSmalltalkInstruction instruction) { m_instruction = instruction; } + TSmalltalkInstruction getInstruction() const { return m_instruction; } + + ControlNode* getArgument(const std::size_t index) const { + assert(index >= 0 && index < m_arguments.size()); + return m_arguments[index]; + } + + void setArgument(const std::size_t index, ControlNode* value) { + if (index >= m_arguments.size()) + m_arguments.resize(index + 1); + m_arguments[index] = value; + } + + uint32_t addArgument(ControlNode* value) { + m_arguments.push_back(value); + return m_arguments.size() - 1; + } + + std::size_t getArgumentsCount() const { return m_arguments.size(); } + + typedef std::vector TArgumentList; + typedef TArgumentList::iterator iterator; + iterator begin() { return m_arguments.begin(); } + iterator end() { return m_arguments.end(); } + +private: + TSmalltalkInstruction m_instruction; + TArgumentList m_arguments; +}; + +// Phi node act as a value aggregator from several domains. +// When value is pushed on the stack in one basic block and +// popped in another we say that actual values have a stack relation. +// +// The main purpose of it is to aggregate several pushes from basic blocks +// followed by a control branch to a signle pop block. When basic blocks +// gets converted to a domain of nodes such relation is encoded by inserting +// a phi node so that instruction that pops a value from the stack is converted +// to a node having a phi node as it's agrument. +class PhiNode : public ControlNode { +public: + virtual TNodeType getNodeType() const { return ntPhi; } + uint32_t getIndex() const { return m_index; } + void setIndex(uint32_t value) { m_index = value; } + +private: + uint32_t m_index; +}; + +// Tau node is reserved for further use in type inference subsystem. +// It will link variable type transitions across a method. +class TauNode : public ControlNode { +public: + virtual TNodeType getNodeType() const { return ntTau; } +}; + +// Domain is a group of nodes within a graph +// that represent a single basic block +class ControlDomain { +public: + typedef TNodeSet::iterator iterator; + iterator begin() { return m_nodes.begin(); } + iterator end() { return m_nodes.end(); } + + void addNode(ControlNode* node) { m_nodes.insert(node); } + void removeNode(ControlNode* node) { m_nodes.erase(node); } + + BasicBlock* getBasicBlock() const { return m_basicBlock; } + void setBasicBlock(BasicBlock* value) { m_basicBlock = value; } + +private: + TNodeSet m_nodes; + BasicBlock* m_basicBlock; +}; + +class ControlGraph { +public: + ControlGraph(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } + + typedef TDomainSet::iterator iterator; + iterator begin() { return m_domains.begin(); } + iterator end() { return m_domains.end(); } + + ControlNode* newNode(ControlNode::TNodeType type) { + ControlNode* node = 0; + + switch (type) { + case ControlNode::ntInstruction: + node = new InstructionNode(); + break; + + case ControlNode::ntPhi: + node = new PhiNode(); + break; + + case ControlNode::ntTau: + node = new TauNode(); + break; + } + + m_nodes.push_back(node); + return node; + } + + template T* newNode(); + + ControlDomain* newDomain() { + ControlDomain* domain = new ControlDomain; + m_domains.insert(domain); + return domain; + } + + ~ControlGraph() { + TDomainSet::iterator iDomain = m_domains.begin(); + while (iDomain != m_domains.end()) + delete * iDomain++; + + TNodeList::iterator iNode = m_nodes.begin(); + while (iNode != m_nodes.end()) + delete * iNode++; + } + +private: + ParsedMethod* m_parsedMethod; + TDomainSet m_domains; + + typedef std::list TNodeList; + TNodeList m_nodes; +}; + +template<> InstructionNode* ControlGraph::newNode(); +template<> PhiNode* ControlGraph::newNode(); +template<> TauNode* ControlGraph::newNode(); + } // namespace st #endif diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp new file mode 100644 index 0000000..3008fd5 --- /dev/null +++ b/src/ControlGraph.cpp @@ -0,0 +1,7 @@ +#include + +using namespace st; + +template<> InstructionNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntInstruction)); } +template<> PhiNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntPhi)); } +template<> TauNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntTau)); } From 72d5992ea09ad70f6bf0f18740e90f414f1dd063 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 17 Apr 2014 17:41:18 +0700 Subject: [PATCH 012/290] Adds first stage of control graph generation procedure Issue: #32 --- include/analysis.h | 74 ++++++++++++++++-- src/ControlGraph.cpp | 179 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+), 5 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index f22bfb3..8969430 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -62,8 +62,9 @@ class BlockReturnDetector : public ParsedBlockVisitor { class ControlDomain; class ControlNode; -typedef std::set TNodeSet; -typedef std::set TDomainSet; +typedef std::vector TNodeList; +typedef std::set TNodeSet; +typedef std::set TDomainSet; // ControlNode is a base class for elements of ControlGraph. // Elements of a graph represent various types of relations @@ -78,6 +79,7 @@ class ControlNode { ntTau // virtual node linkinig variable types from assignment sites }; + ControlNode() : m_domain(0) { } virtual ~ControlNode() { } virtual TNodeType getNodeType() const = 0; @@ -111,7 +113,7 @@ class InstructionNode : public ControlNode { virtual TNodeType getNodeType() const { return ntInstruction; } void setInstruction(TSmalltalkInstruction instruction) { m_instruction = instruction; } - TSmalltalkInstruction getInstruction() const { return m_instruction; } + const TSmalltalkInstruction& getInstruction() const { return m_instruction; } ControlNode* getArgument(const std::size_t index) const { assert(index >= 0 && index < m_arguments.size()); @@ -178,17 +180,61 @@ class ControlDomain { void addNode(ControlNode* node) { m_nodes.insert(node); } void removeNode(ControlNode* node) { m_nodes.erase(node); } + InstructionNode* getEntryPoint() const { return m_entryPoint; } + void setEntryPoint(InstructionNode* value) { m_entryPoint = value; } + + InstructionNode* getTerminator() const { return m_terminator; } + void setTerminator(InstructionNode* value) { m_terminator = value; } + BasicBlock* getBasicBlock() const { return m_basicBlock; } void setBasicBlock(BasicBlock* value) { m_basicBlock = value; } + void pushValue(ControlNode* value) { + m_localStack.push_back(value); + } + + ControlNode* popValue() { + assert(! m_localStack.empty()); + + ControlNode* value = m_localStack.back(); + m_localStack.pop_back(); + return value; + } + + void requestArgument(uint32_t index, InstructionNode* forNode) { + if (! m_localStack.empty()) { + ControlNode* argument = popValue(); + argument->addEdge(forNode); + forNode->setArgument(index, argument); + } else { + m_reqestedArguments.push_back((TArgumentRequest){index, forNode}); + } + } + + struct TArgumentRequest { + uint32_t index; + InstructionNode* requestingNode; + }; + typedef std::vector TRequestList; + + const TRequestList& getRequestedArguments() const { return m_reqestedArguments; } + const TNodeList& getLocalStack() const { return m_localStack; } + + ControlDomain() : m_entryPoint(0), m_terminator(0), m_basicBlock(0) { } private: - TNodeSet m_nodes; - BasicBlock* m_basicBlock; + TNodeSet m_nodes; + InstructionNode* m_entryPoint; + InstructionNode* m_terminator; + BasicBlock* m_basicBlock; + + TNodeList m_localStack; + TRequestList m_reqestedArguments; }; class ControlGraph { public: ControlGraph(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } + ParsedMethod* getParsedMethod() const { return m_parsedMethod; } typedef TDomainSet::iterator iterator; iterator begin() { return m_domains.begin(); } @@ -233,12 +279,30 @@ class ControlGraph { delete * iNode++; } + void buildGraph(); + + ControlDomain* getDomainFor(BasicBlock* basicBlock) { + TDomainMap::iterator iDomain = m_blocksToDomains.find(basicBlock); + if (iDomain == m_blocksToDomains.end()) { + ControlDomain* const domain = newDomain(); + domain->setBasicBlock(basicBlock); + m_blocksToDomains[basicBlock] = domain; + return domain; + } + + assert(iDomain->second); + return iDomain->second; + } + private: ParsedMethod* m_parsedMethod; TDomainSet m_domains; typedef std::list TNodeList; TNodeList m_nodes; + + typedef std::map TDomainMap; + TDomainMap m_blocksToDomains; }; template<> InstructionNode* ControlGraph::newNode(); diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 3008fd5..fa790ff 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -5,3 +5,182 @@ using namespace st; template<> InstructionNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntInstruction)); } template<> PhiNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntPhi)); } template<> TauNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntTau)); } + +class GraphConstructor : public InstructionVisitor { +public: + GraphConstructor(ControlGraph* graph) : InstructionVisitor(graph->getParsedMethod()), m_graph(graph) { } + + virtual bool visitBlock(BasicBlock& basicBlock) { + m_currentDomain = m_graph->getDomainFor(&basicBlock); + return InstructionVisitor::visitBlock(basicBlock); + } + + virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { + // Initializing instruction node + InstructionNode* newNode = m_graph->newNode(); + newNode->setInstruction(instruction); + newNode->setDomain(m_currentDomain); + m_currentDomain->addNode(newNode); + + // Processing instruction by adding references + processNode(newNode); + + return true; + } + +private: + void processNode(InstructionNode* node); + void processSpecials(InstructionNode* node); + void processPrimitives(InstructionNode* node); + + ControlGraph* m_graph; + ControlDomain* m_currentDomain; +}; + +void GraphConstructor::processNode(InstructionNode* node) +{ + const TSmalltalkInstruction& instruction = node->getInstruction(); + + if (! m_currentDomain->getEntryPoint()) + m_currentDomain->setEntryPoint(node); + + switch (instruction.getOpcode()) { + case opcode::pushConstant: + case opcode::pushLiteral: + case opcode::pushArgument: + case opcode::pushTemporary: // TODO Link with tau node + case opcode::pushInstance: + case opcode::pushBlock: + m_currentDomain->pushValue(node); + break; + + case opcode::assignTemporary: // TODO Link with tau node + case opcode::assignInstance: + case opcode::sendUnary: + case opcode::sendMessage: + m_currentDomain->requestArgument(0, node); + break; + + case opcode::sendBinary: + m_currentDomain->requestArgument(1, node); + m_currentDomain->requestArgument(0, node); + break; + + case opcode::markArguments: + for (uint32_t index = instruction.getArgument(); index > 0; ) + m_currentDomain->requestArgument(--index, node); + break; + + case opcode::doSpecial: + processSpecials(node); + break; + + case opcode::doPrimitive: + processPrimitives(node); + break; + + default: + ; // TODO + } +} + +void GraphConstructor::processSpecials(InstructionNode* node) +{ + const TSmalltalkInstruction& instruction = node->getInstruction(); + + switch (instruction.getArgument()) { + case special::stackReturn: + case special::blockReturn: + case special::sendToSuper: + m_currentDomain->requestArgument(0, node); + break; + + case special::duplicate: + m_currentDomain->requestArgument(0, node); + m_currentDomain->pushValue(node); + break; + + case special::popTop: + // m_currentDomain->popValue(); + m_currentDomain->requestArgument(0, node); + break; + + case special::branchIfTrue: + case special::branchIfFalse: + m_currentDomain->requestArgument(0, node); + case special::branch: + assert(! m_currentDomain->getTerminator()); + m_currentDomain->setTerminator(node); + break; + } +} + +void GraphConstructor::processPrimitives(InstructionNode* node) +{ + const TSmalltalkInstruction& instruction = node->getInstruction(); + + switch (instruction.getArgument()) { + case primitive::ioPutChar: + case primitive::getClass: + case primitive::getSize: + case primitive::integerNew: + m_currentDomain->requestArgument(0, node); + break; + + case primitive::objectsAreEqual: + case primitive::startNewProcess: + case primitive::allocateObject: + case primitive::allocateByteArray: + case primitive::cloneByteObject: + + case primitive::arrayAt: + case primitive::stringAt: + + case primitive::smallIntAdd: + case primitive::smallIntDiv: + case primitive::smallIntMod: + case primitive::smallIntLess: + case primitive::smallIntEqual: + case primitive::smallIntMul: + case primitive::smallIntSub: + case primitive::smallIntBitOr: + case primitive::smallIntBitAnd: + case primitive::smallIntBitShift: + + case primitive::LLVMsendMessage: + m_currentDomain->requestArgument(1, node); + m_currentDomain->requestArgument(0, node); + break; + + case primitive::arrayAtPut: + case primitive::stringAtPut: + m_currentDomain->requestArgument(2, node); + m_currentDomain->requestArgument(1, node); + m_currentDomain->requestArgument(0, node); + break; + + case primitive::blockInvoke: + m_currentDomain->requestArgument(0, node); // block object + for (uint32_t index = instruction.getArgument() - 1; index > 0; index--) // FIXME + m_currentDomain->requestArgument(index - 1, node); // arguments + break; + + case primitive::bulkReplace: + for (uint32_t index = 5; index > 0; ) + m_currentDomain->requestArgument(--index, node); + break; + + default: + ; //TODO + } +} + +void ControlGraph::buildGraph() +{ + // Iterating through basic blocks of parsed method and constructing node domains + GraphConstructor constructor(this); + constructor.run(); + + // TODO Linking nodes that requested argument during previous stage. + // Typically they're linked using phi nodes or a direct link if possible. +} From 7de8984e96d8b6f30b9f8738c1d8e62ef94af092 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 18 Apr 2014 15:50:02 +0700 Subject: [PATCH 013/290] Adds DomainVisitor for iterating control graph domains Issue: #32 --- include/analysis.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/analysis.h b/include/analysis.h index 8969430..f2e4e3a 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -309,6 +309,29 @@ template<> InstructionNode* ControlGraph::newNode(); template<> PhiNode* ControlGraph::newNode(); template<> TauNode* ControlGraph::newNode(); +class DomainVisitor { +public: + DomainVisitor(ControlGraph* graph) : m_graph(graph) { } + virtual ~DomainVisitor() { } + + virtual bool visitDomain(ControlDomain& domain) { return true; } + + void run() { + ControlGraph::iterator iDomain = m_graph->begin(); + const ControlGraph::iterator iEnd = m_graph->end(); + + while (iDomain != iEnd) { + if (! visitDomain(** iDomain)) + break; + + ++iDomain; + } + } + +protected: + ControlGraph* m_graph; +}; + } // namespace st #endif From dd5145c8e473783d97f4b1bb6abd6dc11371be28 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 18 Apr 2014 16:01:27 +0700 Subject: [PATCH 014/290] Adds BasicBlock reference API This commit adds reference information to the BasicBlocks so that it knows which basic blocks are referencing it by jumping to the first byte offset of the current basic block. Information is collected during bytecode parsing. Additionally, some functions are added for the TSmalltalkInstruction which helps classifying instructions during iteration. These functions are used in higher level control flow analysis API. Issue: #32 --- include/instructions.h | 51 ++++++++++++++++++++++++++++++++++++++++++ src/ParsedBytecode.cpp | 50 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index 599e7df..ef8cd78 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,39 @@ struct TSmalltalkInstruction { return static_cast(m_opcode) | (m_argument << 8) | (m_extra << 16); } + bool isTerminator() const { + if (m_opcode != opcode::doSpecial) + return false; + + if (isBranch()) + return true; + + switch (m_argument) { + case special::stackReturn: + case special::selfReturn: + case special::blockReturn: + return true; + + default: + return false; + } + } + + bool isBranch() const { + if (m_opcode != opcode::doSpecial) + return false; + + switch (m_argument) { + case special::stackReturn: + case special::selfReturn: + case special::blockReturn: + return true; + + default: + return false; + } + } + private: TOpcode m_opcode; TArgument m_argument; @@ -50,6 +84,7 @@ struct TSmalltalkInstruction { class BasicBlock { public: typedef std::vector TInstructionVector; + typedef std::set TBasicBlockSet; class iterator : public TInstructionVector::iterator { public: @@ -66,6 +101,8 @@ class BasicBlock { iterator begin() { return iterator(m_instructions.begin()); } iterator end() { return iterator(m_instructions.end()); } + std::size_t size() const { return m_instructions.size(); } + const TSmalltalkInstruction operator[](const std::size_t index) const { return TSmalltalkInstruction(m_instructions[index]); } @@ -108,10 +145,24 @@ class BasicBlock { // Offset of first instruction of this basic block within the method's bytecodes uint16_t getOffset() const { return m_offset; } + // Set of basic blocks that are referencing current block by [conditionally] jumping into it + TBasicBlockSet& getReferers() { return m_referers; } + + bool getTerminator(TSmalltalkInstruction& out) const { + TSmalltalkInstruction result(m_instructions.back()); + if (result.isTerminator()) { + out = result; + return true; + } + + return false; + } + BasicBlock(uint16_t blockOffset = 0) : m_offset(blockOffset) { } private: uint16_t m_offset; TInstructionVector m_instructions; + TBasicBlockSet m_referers; }; // This is a base class for ParsedMethod and ParsedBlock diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index be92281..4a65241 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -68,11 +68,57 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { while (bytePointer < stopPointer) { // Switching basic block if current offset is a branch target const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(bytePointer); - if (iBlock != m_offsetToBasicBlock.end()) - currentBasicBlock = iBlock->second; + if (iBlock != m_offsetToBasicBlock.end()) { + BasicBlock* const nextBlock = iBlock->second; + + // Checking if previous block referred current block + // by jumping into it and adding reference if needed + TSmalltalkInstruction terminator(opcode::extended); + if (currentBasicBlock->getTerminator(terminator)) { + if (terminator.isBranch()) { + if (terminator.getExtra() == bytePointer) { + // Unconditional branch case + assert(terminator.getArgument() == special::branch); + nextBlock->getReferers().insert(currentBasicBlock); + } else { + // Previous block referred some other block instead. + // Terminator is one of conditional branch instructions. + // We need to refer both of branch targets here. + assert(terminator.getArgument() == special::branchIfTrue + || terminator.getArgument() == special::branchIfFalse); + + // Case when branch condition is not met + nextBlock->getReferers().insert(currentBasicBlock); + + // Case when branch condition is met + const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); + if (iTargetBlock != m_offsetToBasicBlock.end()) + iTargetBlock->second->getReferers().insert(currentBasicBlock); + else + assert(false); + } + } + } else { + // Adding branch instruction to link blocks + currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, bytePointer)); + nextBlock->getReferers().insert(currentBasicBlock); + } + + // Switching to a new block + currentBasicBlock = nextBlock; + } // Fetching instruction and appending it to the current basic block TSmalltalkInstruction instruction(byteCodes, bytePointer); currentBasicBlock->append(instruction); + + // Final check + if (instruction.isBranch()) { + const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(instruction.getExtra()); + if (iTargetBlock != m_offsetToBasicBlock.end()) + iTargetBlock->second->getReferers().insert(currentBasicBlock); + else + assert(false); + } } } From a687b3b63220a7438dffb00740c961f80866d62c Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 18 Apr 2014 16:25:15 +0700 Subject: [PATCH 015/290] Adds second stage of control graph generation procedure (graph linker) Issue: #32 --- src/ControlGraph.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index fa790ff..4e73e79 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -175,12 +175,68 @@ void GraphConstructor::processPrimitives(InstructionNode* node) } } +class GraphLinker : public DomainVisitor { +public: + GraphLinker(ControlGraph* graph) : DomainVisitor(graph) { } + + virtual bool visitDomain(ControlDomain& domain) { + m_currentDomain = &domain; + + const ControlDomain::TRequestList& requestList = domain.getRequestedArguments(); + for (std::size_t index = 0; index < requestList.size(); index++) + processArgumentRequest(index, requestList[index]); + + return true; + } + +private: + void processArgumentRequest(std::size_t index, const ControlDomain::TArgumentRequest& request); + + ControlDomain* m_currentDomain; +}; + +void GraphLinker::processArgumentRequest(std::size_t index, const ControlDomain::TArgumentRequest& request) +{ + const BasicBlock::TBasicBlockSet& refererBlocks = m_currentDomain->getBasicBlock()->getReferers(); + + // In case of exactly one referer we may link values directly + const bool singleReferer = (refererBlocks.size() == 1); + + // Otherwise we should iterate through all referers and aggregate values using phi node + PhiNode* const phiNode = singleReferer ? 0 : m_graph->newNode(); + + BasicBlock::TBasicBlockSet::iterator iBlock = refererBlocks.begin(); + for (; iBlock != refererBlocks.end(); ++iBlock) { + ControlDomain* const refererDomain = m_graph->getDomainFor(* iBlock); + const TNodeList& refererStack = refererDomain->getLocalStack(); + const std::size_t refererStackSize = refererStack.size(); + + if (index > refererStackSize - 1) { + // TODO Referer block do not have enough values on it's stack. + // We need to go deeper and process it's referers in turn. + } else { + const std::size_t valueIndex = refererStackSize - 1 - index; + ControlNode* value = refererStack[valueIndex]; + + if (singleReferer) + value->addEdge(request.requestingNode); + else + value->addEdge(phiNode); + } + } + + if (! singleReferer) + request.requestingNode->setArgument(request.index, phiNode); +} + void ControlGraph::buildGraph() { // Iterating through basic blocks of parsed method and constructing node domains GraphConstructor constructor(this); constructor.run(); - // TODO Linking nodes that requested argument during previous stage. - // Typically they're linked using phi nodes or a direct link if possible. + // Linking nodes that requested argument during previous stage. + // They're linked using phi nodes or a direct link if possible. + GraphLinker linker(this); + linker.run(); } From d992f8505ec380a9a3f02486a555ad2060a56ffa Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 19 Apr 2014 22:53:06 +0700 Subject: [PATCH 016/290] Linker now adds branching edges to control graph and phi node fix Issue: #32 --- src/ControlGraph.cpp | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 4e73e79..b96360b 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -12,6 +12,7 @@ class GraphConstructor : public InstructionVisitor { virtual bool visitBlock(BasicBlock& basicBlock) { m_currentDomain = m_graph->getDomainFor(&basicBlock); + m_currentDomain->setBasicBlock(&basicBlock); return InstructionVisitor::visitBlock(basicBlock); } @@ -182,20 +183,41 @@ class GraphLinker : public DomainVisitor { virtual bool visitDomain(ControlDomain& domain) { m_currentDomain = &domain; - const ControlDomain::TRequestList& requestList = domain.getRequestedArguments(); - for (std::size_t index = 0; index < requestList.size(); index++) - processArgumentRequest(index, requestList[index]); + processBranching(); + processArgumentRequests(); return true; } private: - void processArgumentRequest(std::size_t index, const ControlDomain::TArgumentRequest& request); + void processBranching() { + // Current domain's entry point should be linked to the terminators of referring domains + InstructionNode* const entryPoint = m_currentDomain->getEntryPoint(); + assert(entryPoint); + + const BasicBlock::TBasicBlockSet& referers = m_currentDomain->getBasicBlock()->getReferers(); + BasicBlock::TBasicBlockSet::iterator iReferer = referers.begin(); + for (; iReferer != referers.end(); ++iReferer) { + ControlDomain* const refererDomain = m_graph->getDomainFor(*iReferer); + InstructionNode* const terminator = refererDomain->getTerminator(); + assert(terminator && terminator->getInstruction().isBranch()); + + terminator->addEdge(entryPoint); + } + } + + void processArgumentRequests() { + const ControlDomain::TRequestList& requestList = m_currentDomain->getRequestedArguments(); + for (std::size_t index = 0; index < requestList.size(); index++) + processRequest(index, requestList[index]); + } + + void processRequest(std::size_t index, const ControlDomain::TArgumentRequest& request); ControlDomain* m_currentDomain; }; -void GraphLinker::processArgumentRequest(std::size_t index, const ControlDomain::TArgumentRequest& request) +void GraphLinker::processRequest(std::size_t index, const ControlDomain::TArgumentRequest& request) { const BasicBlock::TBasicBlockSet& refererBlocks = m_currentDomain->getBasicBlock()->getReferers(); @@ -225,8 +247,11 @@ void GraphLinker::processArgumentRequest(std::size_t index, const ControlDomain: } } - if (! singleReferer) + if (! singleReferer) { + phiNode->setIndex(index); + phiNode->addEdge(request.requestingNode); request.requestingNode->setArgument(request.index, phiNode); + } } void ControlGraph::buildGraph() @@ -237,6 +262,8 @@ void ControlGraph::buildGraph() // Linking nodes that requested argument during previous stage. // They're linked using phi nodes or a direct link if possible. + // Also branching edges are added so graph remains linked even if + // no stack relations exist. GraphLinker linker(this); linker.run(); } From 7443c7984a74d2990437d4f7cfd9d0cc4a172da7 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 20 Apr 2014 01:15:42 +0700 Subject: [PATCH 017/290] Adds NodeVisitor for easy iteration through control graph nodes Issue: #32 --- include/analysis.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/analysis.h b/include/analysis.h index f2e4e3a..50bfd74 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -332,6 +332,27 @@ class DomainVisitor { ControlGraph* m_graph; }; +class NodeVisitor : public DomainVisitor { +public: + NodeVisitor(ControlGraph* graph) : DomainVisitor(graph) { } + virtual bool visitNode(const ControlNode& node) { return true; } + +protected: + virtual bool visitDomain(ControlDomain& domain) { + ControlDomain::iterator iNode = domain.begin(); + const ControlDomain::iterator iEnd = domain.end(); + + while (iNode != iEnd) { + if (! visitNode(** iNode)) + return false; + + ++iNode; + } + + return true; + } +}; + } // namespace st #endif From 5b694f04f5b2f794a479921691591d1aaa487433 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 20 Apr 2014 11:11:24 +0700 Subject: [PATCH 018/290] Adds global node index parameter to aid iterators std::set<> container stores items in an particular order which is determined by provided comparator or default std::less. We compare nodes by their indices that are sequentially assigned by ControlGraph upon node creation. See the NodeIndexCompare functor. This ensures that node iterators and visitors will provide nodes in sequence of their creation or in other sequence which may be defined by specifying custom comparator for separate containers. Issue: #32 --- include/analysis.h | 35 +++++++++++++++++++++++++---------- src/ControlGraph.cpp | 7 ++++++- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 50bfd74..f3ec250 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -62,10 +62,17 @@ class BlockReturnDetector : public ParsedBlockVisitor { class ControlDomain; class ControlNode; + typedef std::vector TNodeList; -typedef std::set TNodeSet; typedef std::set TDomainSet; +class NodeIndexCompare { +public: + bool operator() (const ControlNode* a, const ControlNode* b); +}; + +typedef std::set TNodeSet; + // ControlNode is a base class for elements of ControlGraph. // Elements of a graph represent various types of relations // of code, data and metainfo. Each node is linked to other @@ -79,10 +86,12 @@ class ControlNode { ntTau // virtual node linkinig variable types from assignment sites }; - ControlNode() : m_domain(0) { } + ControlNode(uint32_t index) : m_index(index), m_domain(0) { } virtual ~ControlNode() { } virtual TNodeType getNodeType() const = 0; + uint32_t getIndex() const { return m_index; } + ControlDomain* getDomain() const { return m_domain; } void setDomain(ControlDomain* value) { m_domain = value; } @@ -99,6 +108,7 @@ class ControlNode { to->getInEdges().erase(this); } private: + uint32_t m_index; TNodeType m_type; TNodeSet m_inEdges; TNodeSet m_outEdges; @@ -109,7 +119,7 @@ class ControlNode { // Instruction node represents a signle VM instruction and it's relations in code. class InstructionNode : public ControlNode { public: - InstructionNode() : m_instruction(opcode::extended) { } + InstructionNode(uint32_t index) : ControlNode(index), m_instruction(opcode::extended) { } virtual TNodeType getNodeType() const { return ntInstruction; } void setInstruction(TSmalltalkInstruction instruction) { m_instruction = instruction; } @@ -154,18 +164,20 @@ class InstructionNode : public ControlNode { // to a node having a phi node as it's agrument. class PhiNode : public ControlNode { public: + PhiNode(uint32_t index) : ControlNode(index) { } virtual TNodeType getNodeType() const { return ntPhi; } - uint32_t getIndex() const { return m_index; } - void setIndex(uint32_t value) { m_index = value; } + uint32_t getPhiIndex() const { return m_phiIndex; } + void setPhiIndex(uint32_t value) { m_phiIndex = value; } private: - uint32_t m_index; + uint32_t m_phiIndex; }; // Tau node is reserved for further use in type inference subsystem. // It will link variable type transitions across a method. class TauNode : public ControlNode { public: + TauNode(uint32_t index) : ControlNode(index) { } virtual TNodeType getNodeType() const { return ntTau; } }; @@ -233,7 +245,7 @@ class ControlDomain { class ControlGraph { public: - ControlGraph(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } + ControlGraph(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod), m_lastNodeIndex(0) { } ParsedMethod* getParsedMethod() const { return m_parsedMethod; } typedef TDomainSet::iterator iterator; @@ -245,18 +257,20 @@ class ControlGraph { switch (type) { case ControlNode::ntInstruction: - node = new InstructionNode(); + node = new InstructionNode(m_lastNodeIndex); break; case ControlNode::ntPhi: - node = new PhiNode(); + node = new PhiNode(m_lastNodeIndex); break; case ControlNode::ntTau: - node = new TauNode(); + node = new TauNode(m_lastNodeIndex); break; } + m_lastNodeIndex++; + m_nodes.push_back(node); return node; } @@ -300,6 +314,7 @@ class ControlGraph { typedef std::list TNodeList; TNodeList m_nodes; + uint32_t m_lastNodeIndex; typedef std::map TDomainMap; TDomainMap m_blocksToDomains; diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index b96360b..0c96748 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -2,6 +2,11 @@ using namespace st; +bool NodeIndexCompare::operator() (const ControlNode* a, const ControlNode* b) +{ + return a->getIndex() < b->getIndex(); +} + template<> InstructionNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntInstruction)); } template<> PhiNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntPhi)); } template<> TauNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntTau)); } @@ -248,7 +253,7 @@ void GraphLinker::processRequest(std::size_t index, const ControlDomain::TArgume } if (! singleReferer) { - phiNode->setIndex(index); + phiNode->setPhiIndex(index); phiNode->addEdge(request.requestingNode); request.requestingNode->setArgument(request.index, phiNode); } From 34afcde7d8d466666df42a5c9a22791ab51cca3d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 20 Apr 2014 13:12:49 +0700 Subject: [PATCH 019/290] Linker now add edges to link node islands within domain Issue: #32 --- include/analysis.h | 2 +- src/ControlGraph.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index f3ec250..759484a 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -350,7 +350,7 @@ class DomainVisitor { class NodeVisitor : public DomainVisitor { public: NodeVisitor(ControlGraph* graph) : DomainVisitor(graph) { } - virtual bool visitNode(const ControlNode& node) { return true; } + virtual bool visitNode(ControlNode& node) { return true; } protected: virtual bool visitDomain(ControlDomain& domain) { diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 0c96748..37cd248 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -181,9 +181,9 @@ void GraphConstructor::processPrimitives(InstructionNode* node) } } -class GraphLinker : public DomainVisitor { +class GraphLinker : public NodeVisitor { public: - GraphLinker(ControlGraph* graph) : DomainVisitor(graph) { } + GraphLinker(ControlGraph* graph) : NodeVisitor(graph), m_currentDomain(0), m_nodeToLink(0) { } virtual bool visitDomain(ControlDomain& domain) { m_currentDomain = &domain; @@ -191,10 +191,45 @@ class GraphLinker : public DomainVisitor { processBranching(); processArgumentRequests(); + return NodeVisitor::visitDomain(domain); + } + + virtual bool visitNode(ControlNode& node) { + // Some nodes within a domain are not linked by out edges + // to the rest of the domain nodes with higher indices. + + processNode(node); return true; } private: + void processNode(ControlNode& node) { + // In order to keep graph strongly connected, we link + // node to the next one. This edge would be interpreted + // as a control flow edge, not the stack value flow edge. + + // Linking pending node + if (m_nodeToLink) { + m_nodeToLink->addEdge(&node); + m_nodeToLink = 0; + } + + TNodeSet& outEdges = node.getOutEdges(); + TNodeSet::iterator iNode = outEdges.begin(); + bool isNodeLinked = false; + for (; iNode != outEdges.end(); ++iNode) { + // Checking for connectivity + if ((*iNode)->getDomain() == node.getDomain() && (*iNode)->getIndex() > node.getIndex()) { + // Node is linked. No need to worry. + isNodeLinked = true; + break; + } + } + + if (! isNodeLinked) + m_nodeToLink = &node; + } + void processBranching() { // Current domain's entry point should be linked to the terminators of referring domains InstructionNode* const entryPoint = m_currentDomain->getEntryPoint(); @@ -220,6 +255,7 @@ class GraphLinker : public DomainVisitor { void processRequest(std::size_t index, const ControlDomain::TArgumentRequest& request); ControlDomain* m_currentDomain; + ControlNode* m_nodeToLink; }; void GraphLinker::processRequest(std::size_t index, const ControlDomain::TArgumentRequest& request) From 87b918ab195f53353f49640380cc5038cce80c97 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 22 Apr 2014 22:46:09 +0700 Subject: [PATCH 020/290] Adds recursive domain processing in GraphLinker Issue: #32 --- src/ControlGraph.cpp | 47 ++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 37cd248..fbf6c5e 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -249,24 +249,29 @@ class GraphLinker : public NodeVisitor { void processArgumentRequests() { const ControlDomain::TRequestList& requestList = m_currentDomain->getRequestedArguments(); for (std::size_t index = 0; index < requestList.size(); index++) - processRequest(index, requestList[index]); + processRequest(m_currentDomain, index, requestList[index]); } - void processRequest(std::size_t index, const ControlDomain::TArgumentRequest& request); + void processRequest(ControlDomain* domain, std::size_t argumentIndex, const ControlDomain::TArgumentRequest& request) { + ControlNode* node = getRequestedNode(domain, argumentIndex); + node->addEdge(request.requestingNode); + request.requestingNode->setArgument(request.index, node); + } + + ControlNode* getRequestedNode(ControlDomain* domain, std::size_t index); ControlDomain* m_currentDomain; ControlNode* m_nodeToLink; }; -void GraphLinker::processRequest(std::size_t index, const ControlDomain::TArgumentRequest& request) +ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t argumentIndex) { - const BasicBlock::TBasicBlockSet& refererBlocks = m_currentDomain->getBasicBlock()->getReferers(); + const BasicBlock::TBasicBlockSet& refererBlocks = domain->getBasicBlock()->getReferers(); // In case of exactly one referer we may link values directly - const bool singleReferer = (refererBlocks.size() == 1); - // Otherwise we should iterate through all referers and aggregate values using phi node - PhiNode* const phiNode = singleReferer ? 0 : m_graph->newNode(); + const bool singleReferer = (refererBlocks.size() == 1); + ControlNode* result = singleReferer ? 0 : m_graph->newNode(); BasicBlock::TBasicBlockSet::iterator iBlock = refererBlocks.begin(); for (; iBlock != refererBlocks.end(); ++iBlock) { @@ -274,25 +279,29 @@ void GraphLinker::processRequest(std::size_t index, const ControlDomain::TArgume const TNodeList& refererStack = refererDomain->getLocalStack(); const std::size_t refererStackSize = refererStack.size(); - if (index > refererStackSize - 1) { - // TODO Referer block do not have enough values on it's stack. - // We need to go deeper and process it's referers in turn. + if (argumentIndex > refererStackSize - 1) { + // Referer block do not have enough values on it's stack. + // We need to go deeper and process it's referers in turn. + ControlNode* refererValue = getRequestedNode(refererDomain, argumentIndex); + + if (singleReferer) + result = refererValue; + else + refererValue->addEdge(result); + } else { - const std::size_t valueIndex = refererStackSize - 1 - index; - ControlNode* value = refererStack[valueIndex]; + const std::size_t valueIndex = refererStackSize - 1 - argumentIndex; + ControlNode* stackValue = refererStack[valueIndex]; if (singleReferer) - value->addEdge(request.requestingNode); + result = stackValue; else - value->addEdge(phiNode); + stackValue->addEdge(result); } } - if (! singleReferer) { - phiNode->setPhiIndex(index); - phiNode->addEdge(request.requestingNode); - request.requestingNode->setArgument(request.index, phiNode); - } + assert(result); + return result; } void ControlGraph::buildGraph() From 3660222d387d72d1b84bd052055a7805380572af Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 19 Apr 2014 12:11:45 +0700 Subject: [PATCH 021/290] Adds InstructionDecoder class It is a helper class that performs instruction decoding in an explicit manner. Previous implementation were encapsulated in TSmalltalkInstruction's decoding constructor which was not intuitive. Explicit use of InstructionDecoder is recomended in places that iterate through bytecode instructions. Issue: #32 --- CMakeLists.txt | 1 + include/instructions.h | 21 +++++++++++++ src/InstructionDecoder.cpp | 63 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/InstructionDecoder.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f86cf6..53c302e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ set(CPP_FILES src/vm.cpp src/TSmalltalkInstruction.cpp + src/InstructionDecoder.cpp src/ParsedMethod.cpp src/ParsedBytecode.cpp src/ParsedBlock.cpp diff --git a/include/instructions.h b/include/instructions.h index ef8cd78..5639a20 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -81,6 +81,27 @@ struct TSmalltalkInstruction { TExtra m_extra; }; +class InstructionDecoder { +public: + InstructionDecoder(const TByteObject& byteCodes, uint16_t bytePointer = 0) + : m_byteCodes(byteCodes), m_bytePointer(bytePointer) {} + + uint16_t getBytePointer() const { return m_bytePointer; } + void setBytePointer(uint16_t value) { + assert(value < m_byteCodes.getSize()); + m_bytePointer = value; + } + + const TSmalltalkInstruction decodeAndShiftPointer() { + return decodeAndShiftPointer(m_byteCodes, m_bytePointer); + } + + static const TSmalltalkInstruction decodeAndShiftPointer(const TByteObject& byteCodes, uint16_t& bytePointer); +private: + const TByteObject& m_byteCodes; + uint16_t m_bytePointer; +}; + class BasicBlock { public: typedef std::vector TInstructionVector; diff --git a/src/InstructionDecoder.cpp b/src/InstructionDecoder.cpp new file mode 100644 index 0000000..50b31d0 --- /dev/null +++ b/src/InstructionDecoder.cpp @@ -0,0 +1,63 @@ +#include + +using namespace st; + +const TSmalltalkInstruction InstructionDecoder::decodeAndShiftPointer(const TByteObject& byteCodes, uint16_t& bytePointer) +{ + TSmalltalkInstruction::TOpcode opcode; + TSmalltalkInstruction::TArgument argument; + TSmalltalkInstruction::TExtra extra = 0; + + assert(bytePointer < byteCodes.getSize()); + const uint8_t& bytecode = byteCodes[bytePointer++]; + + // For normal bytecodes higher part of the byte holds opcode + // whether lower part holds the argument + opcode = static_cast(bytecode >> 4); + argument = bytecode & 0x0F; + + // Extended opcodes encode argument in a separate byte + // Opcode is stored in a lower half of the first byte + if (opcode == opcode::extended) { + opcode = static_cast(argument); + argument = byteCodes[bytePointer++]; + } + + // Some instructions hold extra data in a bytes right after instruction + switch (opcode) { + case opcode::pushBlock: + // Storing bytecode offset as extra + extra = byteCodes[bytePointer] | (byteCodes[bytePointer+1] << 8); + bytePointer += 2; + break; + + case opcode::doPrimitive: + // Primitive number do not fit into lower 4 bits of opcode byte. + // So it is stored in a separate byte right after. Technically, + // this value is an argument for instruction so it would be logical + // to hold it in the argument field. + argument = byteCodes[bytePointer++]; + break; + + case opcode::doSpecial: + switch (argument) { + case special::branch: + case special::branchIfTrue: + case special::branchIfFalse: + // Storing jump target offset as extra + extra = byteCodes[bytePointer] | (byteCodes[bytePointer+1] << 8); + bytePointer += 2; + break; + + case special::sendToSuper: + extra = byteCodes[bytePointer++]; + break; + } + break; + + default: // Nothing to do here + break; + } + + return TSmalltalkInstruction(opcode, argument, extra); +} From b8db5ad0b3ca97e8e1b13b781abf62d667697694 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 19 Apr 2014 12:41:21 +0700 Subject: [PATCH 022/290] Refactors ParsedBytecode to use InstructionDecoder Issue: #32 --- src/ParsedBytecode.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 4a65241..dd70db1 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -5,20 +5,19 @@ using namespace st; void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { assert(m_origin && m_origin->byteCodes); - uint16_t bytePointer = startOffset; + InstructionDecoder decoder(*m_origin->byteCodes, startOffset); TByteObject& byteCodes = * m_origin->byteCodes; const uint16_t stopPointer = stopOffset ? stopOffset : byteCodes.getSize(); // Scaning the method's bytecodes for branch sites and collecting branch targets. // Creating target basic blocks beforehand and collecting them in a map. - while (bytePointer < stopPointer) { - // Decoding instruction and shifting byte pointer to the next one - TSmalltalkInstruction instruction(byteCodes, bytePointer); + while (decoder.getBytePointer() < stopPointer) { + TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); if (instruction.getOpcode() == opcode::pushBlock) { // Preserving the start block's offset - const uint16_t startOffset = bytePointer; + const uint16_t startOffset = decoder.getBytePointer(); // Extra holds the bytecode offset right after the block const uint16_t stopOffset = instruction.getExtra(); @@ -29,7 +28,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { parseBlock(startOffset, stopOffset); // Skipping the nested block's bytecodes - bytePointer = stopOffset; + decoder.setBytePointer(stopOffset); continue; } @@ -47,7 +46,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { if (m_offsetToBasicBlock.find(targetOffset) == m_offsetToBasicBlock.end()) { // Creating the referred basic block and inserting it into the function // Later it will be filled with instructions and linked to other blocks - BasicBlock* targetBasicBlock = createBasicBlock(bytePointer); + BasicBlock* targetBasicBlock = createBasicBlock(decoder.getBytePointer()); m_offsetToBasicBlock[targetOffset] = targetBasicBlock; } } break; @@ -65,9 +64,10 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { m_basicBlocks.push_front(currentBasicBlock); } - while (bytePointer < stopPointer) { + decoder.setBytePointer(startOffset); + while (decoder.getBytePointer() < stopPointer) { // Switching basic block if current offset is a branch target - const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(bytePointer); + const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(decoder.getBytePointer()); if (iBlock != m_offsetToBasicBlock.end()) { BasicBlock* const nextBlock = iBlock->second; @@ -76,7 +76,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { TSmalltalkInstruction terminator(opcode::extended); if (currentBasicBlock->getTerminator(terminator)) { if (terminator.isBranch()) { - if (terminator.getExtra() == bytePointer) { + if (terminator.getExtra() == decoder.getBytePointer()) { // Unconditional branch case assert(terminator.getArgument() == special::branch); nextBlock->getReferers().insert(currentBasicBlock); @@ -100,7 +100,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { } } else { // Adding branch instruction to link blocks - currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, bytePointer)); + currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, decoder.getBytePointer())); nextBlock->getReferers().insert(currentBasicBlock); } @@ -109,7 +109,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { } // Fetching instruction and appending it to the current basic block - TSmalltalkInstruction instruction(byteCodes, bytePointer); + TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); currentBasicBlock->append(instruction); // Final check From fe3a9c25ec53214a6dd05cb8d6846a30d74fed09 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 24 Mar 2014 21:39:09 +0700 Subject: [PATCH 023/290] Removes obsolete TSmalltalkInstruction's decoding constructor Issue: #32 --- CMakeLists.txt | 1 - include/instructions.h | 4 --- src/TSmalltalkInstruction.cpp | 57 ----------------------------------- 3 files changed, 62 deletions(-) delete mode 100644 src/TSmalltalkInstruction.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 53c302e..203b3b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,6 @@ set(CPP_FILES src/TSymbol.cpp src/vm.cpp - src/TSmalltalkInstruction.cpp src/InstructionDecoder.cpp src/ParsedMethod.cpp src/ParsedBytecode.cpp diff --git a/include/instructions.h b/include/instructions.h index 5639a20..dda8c94 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -29,10 +29,6 @@ struct TSmalltalkInstruction { m_extra = static_cast((bytecode >> 16) & 0xFF); } - // Decode instruction from method bytecode - // Shifts bytePointer to the next instruction - TSmalltalkInstruction(const TByteObject& byteCodes, uint16_t& bytePointer); - TOpcode getOpcode() const { return m_opcode; } TArgument getArgument() const { return m_argument; } TExtra getExtra() const { return m_extra; } diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp deleted file mode 100644 index a3bb89f..0000000 --- a/src/TSmalltalkInstruction.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include - -using namespace st; - -TSmalltalkInstruction::TSmalltalkInstruction(const TByteObject& byteCodes, uint16_t& bytePointer) - : m_opcode(opcode::extended), m_argument(0), m_extra(0) -{ - const uint8_t& bytecode = byteCodes[bytePointer++]; - - // For normal bytecodes higher part of the byte holds opcode - // whether lower part holds the argument - m_opcode = static_cast(bytecode >> 4); - m_argument = bytecode & 0x0F; - - // Extended opcodes encode argument in a separate byte - // Opcode is stored in a lower half of the first byte - if (m_opcode == opcode::extended) { - m_opcode = static_cast(m_argument); - m_argument = byteCodes[bytePointer++]; - } - - // Some instructions hold extra data in a bytes right after instruction - switch (m_opcode) { - case opcode::pushBlock: - // Storing bytecode offset as extra - m_extra = byteCodes[bytePointer] | (byteCodes[bytePointer+1] << 8); - bytePointer += 2; - break; - - case opcode::doPrimitive: - // Primitive number do not fit into lower 4 bits of opcode byte. - // So it is stored in a separate byte right after. Technically, - // this value is an argument for instruction so it would be logical - // to hold it in the argument field. - m_argument = byteCodes[bytePointer++]; - break; - - case opcode::doSpecial: - switch (m_argument) { - case special::branch: - case special::branchIfTrue: - case special::branchIfFalse: - // Storing jump target offset as extra - m_extra = byteCodes[bytePointer] | (byteCodes[bytePointer+1] << 8); - bytePointer += 2; - break; - - case special::sendToSuper: - m_extra = byteCodes[bytePointer++]; - break; - } - break; - - default: // Nothing to do here - break; - } -} From cd5abb60b05845add701c332189c50d8a98f6232 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 24 Mar 2014 21:40:44 +0700 Subject: [PATCH 024/290] Refactors soft VM to use TSmalltalkInstruction Issue: #32 --- include/vm.h | 6 ++-- src/vm.cpp | 78 ++++++++++++++++++++++------------------------------ 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/include/vm.h b/include/vm.h index 44f5e4f..cd05e68 100644 --- a/include/vm.h +++ b/include/vm.h @@ -39,6 +39,7 @@ #include #include +#include template struct Int2Type @@ -74,8 +75,8 @@ class SmalltalkVM { public: hptr currentContext; - TInstruction instruction; - uint32_t bytePointer; + st::TSmalltalkInstruction instruction; + uint16_t bytePointer; uint32_t stackTop; hptr returnedValue; @@ -108,6 +109,7 @@ class SmalltalkVM { TVMExecutionContext(IMemoryManager* mm, SmalltalkVM* vm) : m_vm(vm), currentContext( static_cast(globals.nilObject), mm), + instruction(opcode::extended), returnedValue(globals.nilObject, mm) { } }; diff --git a/src/vm.cpp b/src/vm.cpp index 6ab7866..90c5bf0 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -39,7 +39,6 @@ #include #include -#include #include #include #include @@ -254,26 +253,22 @@ SmalltalkVM::TExecuteResult SmalltalkVM::execute(TProcess* p, uint32_t ticks) } // Decoding the instruction - ec.instruction.low = (ec.instruction.high = byteCodes[ec.bytePointer++]) & 0x0F; - ec.instruction.high >>= 4; - if (ec.instruction.high == opcode::extended) { - ec.instruction.high = ec.instruction.low; - ec.instruction.low = byteCodes[ec.bytePointer++]; - } + const uint16_t lastBytePointer = ec.bytePointer; + ec.instruction = st::InstructionDecoder::decodeAndShiftPointer(byteCodes, ec.bytePointer); // And executing it - switch (ec.instruction.high) { - case opcode::pushInstance: ec.stackPush(instanceVariables[ec.instruction.low]); break; - case opcode::pushArgument: ec.stackPush(arguments[ec.instruction.low]); break; - case opcode::pushTemporary: ec.stackPush(temporaries[ec.instruction.low]); break; - case opcode::pushLiteral: ec.stackPush(literals[ec.instruction.low]); break; + switch (ec.instruction.getOpcode()) { + case opcode::pushInstance: ec.stackPush(instanceVariables[ec.instruction.getArgument()]); break; + case opcode::pushArgument: ec.stackPush(arguments[ec.instruction.getArgument()]); break; + case opcode::pushTemporary: ec.stackPush(temporaries[ec.instruction.getArgument()]); break; + case opcode::pushLiteral: ec.stackPush(literals[ec.instruction.getArgument()]); break; case opcode::pushConstant: doPushConstant(ec); break; case opcode::pushBlock: doPushBlock(ec); break; - case opcode::assignTemporary: temporaries[ec.instruction.low] = ec.stackLast(); break; + case opcode::assignTemporary: temporaries[ec.instruction.getArgument()] = ec.stackLast(); break; case opcode::assignInstance: { TObject* newValue = ec.stackLast(); - TObject** objectSlot = & instanceVariables[ec.instruction.low]; + TObject** objectSlot = & instanceVariables[ec.instruction.getArgument()]; // Checking whether we need to register current object slot in the GC checkRoot(newValue, objectSlot); @@ -301,7 +296,7 @@ SmalltalkVM::TExecuteResult SmalltalkVM::execute(TProcess* p, uint32_t ticks) } break; default: - std::fprintf(stderr, "VM: Invalid opcode %d at offset %d in method ", ec.instruction.high, ec.bytePointer); + std::fprintf(stderr, "VM: Invalid opcode %d at offset %d in method ", ec.instruction.getOpcode(), lastBytePointer); std::fprintf(stderr, "'%s'\n", ec.currentContext->method->name->toString().c_str() ); std::exit(1); } @@ -323,11 +318,8 @@ void SmalltalkVM::doPushBlock(TVMExecutionContext& ec) // right after the block's body. There we'll probably find the actual invoking code // such as sendMessage to a receiver with our block as a parameter or something similar. - // Reading new byte pointer that points to the code right after the inline block - uint16_t newBytePointer = byteCodes[ec.bytePointer] | (byteCodes[ec.bytePointer+1] << 8); - - // Skipping the newBytePointer's data - ec.bytePointer += 2; + // New byte pointer that points to the code right after the inline block + uint16_t newBytePointer = ec.instruction.getExtra(); // Creating block object hptr newBlock = newObject(); @@ -336,10 +328,10 @@ void SmalltalkVM::doPushBlock(TVMExecutionContext& ec) uint32_t stackSize = ec.currentContext->method->stackSize; newBlock->stack = newObject(stackSize/*, false*/); - newBlock->argumentLocation = ec.instruction.low; + newBlock->argumentLocation = ec.instruction.getArgument(); newBlock->blockBytePointer = ec.bytePointer; - //We set block->bytePointer, stackTop, previousContext when block is invoked + // We set block->bytePointer, stackTop, previousContext when block is invoked // Assigning creatingContext depending on the hierarchy // Nested blocks inherit the outer creating context @@ -361,12 +353,12 @@ void SmalltalkVM::doPushBlock(TVMExecutionContext& ec) void SmalltalkVM::doMarkArguments(TVMExecutionContext& ec) { - hptr args = newObject(ec.instruction.low); + hptr args = newObject(ec.instruction.getArgument()); - // This operation takes instruction.low arguments - // from the top of the stack and creates new array with them + // This operation takes specified amount of arguments + // from top of the stack and creates new array with them - uint32_t index = ec.instruction.low; + uint32_t index = ec.instruction.getArgument(); while (index > 0) args[--index] = ec.stackPop(); @@ -468,7 +460,7 @@ void SmalltalkVM::doSendMessage(TVMExecutionContext& ec) // These do not need to be hptr'ed TSymbolArray& literals = * ec.currentContext->method->literals; - TSymbol* messageSelector = literals[ec.instruction.low]; + TSymbol* messageSelector = literals[ec.instruction.getArgument()]; doSendMessage(ec, messageSelector, messageArguments); } @@ -477,12 +469,12 @@ void SmalltalkVM::doSendUnary(TVMExecutionContext& ec) { TObject* top = ec.stackPop(); - switch ( static_cast(ec.instruction.low) ) { + switch ( static_cast(ec.instruction.getArgument()) ) { case unaryBuiltIns::isNil : ec.returnedValue = (top == globals.nilObject) ? globals.trueObject : globals.falseObject; break; case unaryBuiltIns::notNil : ec.returnedValue = (top != globals.nilObject) ? globals.trueObject : globals.falseObject; break; default: - std::fprintf(stderr, "VM: Invalid opcode %d passed to sendUnary\n", ec.instruction.low); + std::fprintf(stderr, "VM: Invalid opcode %d passed to sendUnary\n", ec.instruction.getArgument()); std::exit(1); } @@ -505,7 +497,7 @@ void SmalltalkVM::doSendBinary(TVMExecutionContext& ec) bool unusedCondition; // Performing an operation - switch ( static_cast(ec.instruction.low) ) { + switch ( static_cast(ec.instruction.getArgument()) ) { case binaryBuiltIns::operatorLess: ec.returnedValue = (leftOperand < rightOperand) ? globals.trueObject : globals.falseObject; break; @@ -519,7 +511,7 @@ void SmalltalkVM::doSendBinary(TVMExecutionContext& ec) break; default: - std::fprintf(stderr, "VM: Invalid opcode %d passed to sendBinary\n", ec.instruction.low); + std::fprintf(stderr, "VM: Invalid opcode %d passed to sendBinary\n", ec.instruction.getArgument()); std::exit(1); } @@ -537,7 +529,7 @@ void SmalltalkVM::doSendBinary(TVMExecutionContext& ec) messageArguments[1] = pRightObject; messageArguments[0] = pLeftObject; - TSymbol* messageSelector = static_cast( globals.binaryMessages[ec.instruction.low] ); + TSymbol* messageSelector = static_cast( globals.binaryMessages[ec.instruction.getArgument()] ); doSendMessage(ec, messageSelector, messageArguments); } } @@ -548,7 +540,7 @@ SmalltalkVM::TExecuteResult SmalltalkVM::doSpecial(hptr& process, TVME TObjectArray& arguments = * ec.currentContext->arguments; TSymbolArray& literals = * ec.currentContext->method->literals; - switch(ec.instruction.low) + switch(ec.instruction.getArgument()) { case special::selfReturn: { ec.returnedValue = arguments[0]; // arguments[0] always keep self @@ -604,29 +596,25 @@ SmalltalkVM::TExecuteResult SmalltalkVM::doSpecial(hptr& process, TVME break; case special::branch: - ec.bytePointer = byteCodes[ec.bytePointer] | (byteCodes[ec.bytePointer+1] << 8); + ec.bytePointer = ec.instruction.getExtra(); break; case special::branchIfTrue: { ec.returnedValue = ec.stackPop(); if (ec.returnedValue == globals.trueObject) - ec.bytePointer = byteCodes[ec.bytePointer] | (byteCodes[ec.bytePointer+1] << 8); - else - ec.bytePointer += 2; + ec.bytePointer = ec.instruction.getExtra(); } break; case special::branchIfFalse: { ec.returnedValue = ec.stackPop(); if (ec.returnedValue == globals.falseObject) - ec.bytePointer = byteCodes[ec.bytePointer] | (byteCodes[ec.bytePointer+1] << 8); - else - ec.bytePointer += 2; + ec.bytePointer = ec.instruction.getExtra(); } break; case special::sendToSuper: { - uint32_t literalIndex = byteCodes[ec.bytePointer++]; + uint32_t literalIndex = ec.instruction.getExtra(); TSymbol* messageSelector = literals[literalIndex]; TClass* receiverClass = ec.currentContext->method->klass->parentClass; TObjectArray* messageArguments = ec.stackPop(); @@ -640,7 +628,7 @@ SmalltalkVM::TExecuteResult SmalltalkVM::doSpecial(hptr& process, TVME void SmalltalkVM::doPushConstant(TVMExecutionContext& ec) { - uint8_t constant = ec.instruction.low; + uint8_t constant = ec.instruction.getArgument(); switch (constant) { case 0: @@ -668,7 +656,7 @@ void SmalltalkVM::doPushConstant(TVMExecutionContext& ec) SmalltalkVM::TExecuteResult SmalltalkVM::doPrimitive(hptr& process, TVMExecutionContext& ec) { - uint8_t opcode = (*ec.currentContext->method->byteCodes)[ec.bytePointer++]; + uint8_t opcode = ec.instruction.getArgument(); // First of all, executing the primitive // If primitive succeeds then stop execution of the current method @@ -834,7 +822,7 @@ TObject* SmalltalkVM::performPrimitive(uint8_t opcode, hptr& process, uint32_t argumentLocation = block->argumentLocation; // Amount of arguments stored on the stack except the block itself - uint32_t argCount = ec.instruction.low - 1; + uint32_t argCount = ec.instruction.getArgument() - 1; // Checking the passed temps size TObjectArray* blockTemps = block->temporaries; @@ -1014,7 +1002,7 @@ TObject* SmalltalkVM::performPrimitive(uint8_t opcode, hptr& process, case primitive::getSystemTicks: //253 default: { - uint32_t argCount = ec.instruction.low; + uint32_t argCount = ec.instruction.getArgument(); hptr args = newObject(argCount); uint32_t i = argCount; From ffb701486324fc9b0858366636baebf20ab4c792 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 25 Mar 2014 22:27:30 +0700 Subject: [PATCH 025/290] Adds ParsedMethod to JIT context Redundant fields will be refactored later Issue: #32 --- include/jit.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/jit.h b/include/jit.h index 315cacf..ea49567 100644 --- a/include/jit.h +++ b/include/jit.h @@ -220,6 +220,8 @@ class MethodCompiler { // This structure contains working data which is // used during the compilation process. struct TJITContext { + st::ParsedMethod parsedMethod; // Parsed smalltalk method we're currently processing + TMethod* method; // Smalltalk method we're currently processing uint32_t bytePointer; @@ -251,7 +253,7 @@ class MethodCompiler { void pushValue(TStackValue* value); TJITContext(MethodCompiler* compiler, TMethod* method) - : method(method), bytePointer(0), function(0), builder(0), + : parsedMethod(method), method(method), bytePointer(0), function(0), builder(0), preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), contextHolder(0), selfHolder(0) { From 40325f3a6a80dce2a42d9005bf908f2c8e2ec7a2 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 29 Apr 2014 21:47:25 +0700 Subject: [PATCH 026/290] Refactors MethodCompiler::scanForBlockReturn() using BlockReturnDetector Issue: #32 --- src/MethodCompiler.cpp | 68 ++++-------------------------------------- 1 file changed, 6 insertions(+), 62 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index e7e8838..1ca9cc8 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -39,6 +39,7 @@ #include #include #include +#include using namespace llvm; @@ -362,75 +363,18 @@ Value* MethodCompiler::TJITContext::getSelf() bool MethodCompiler::scanForBlockReturn(TJITContext& jit, uint32_t byteCount/* = 0*/) { - // This pass is used to find out whether method code contains block return instruction. + // Here we trying to find out whether method code contains block return instruction. // This instruction is handled in a very different way than the usual opcodes. // Thus requires special handling. Block return is done by trowing an exception out of // the block containing it. Then it's catched by the method's code to perform a return. // In order not to bloat the code with unused try-catch code we're previously scanning // the method's code to ensure that try-catch is really needed. If it is not, we simply - // skip its generation. Note that we need to scan not only the actual method code but - // also every nested block, because typically block return is located there. + // skip its generation. - uint32_t previousBytePointer = jit.bytePointer; - - TByteObject& byteCodes = * jit.method->byteCodes; - uint32_t stopPointer = jit.bytePointer + (byteCount ? byteCount : byteCodes.getSize()); - - // Processing the method's bytecodes - while (jit.bytePointer < stopPointer) { - // Decoding the pending instruction (TODO move to a function) - TInstruction instruction; - instruction.low = (instruction.high = byteCodes[jit.bytePointer++]) & 0x0F; - instruction.high >>= 4; - if (instruction.high == opcode::extended) { - instruction.high = instruction.low; - instruction.low = byteCodes[jit.bytePointer++]; - } + st::BlockReturnDetector detector(&jit.parsedMethod); + detector.run(); - if (instruction.high == opcode::pushBlock) { - uint16_t newBytePointer = byteCodes[jit.bytePointer] | (byteCodes[jit.bytePointer+1] << 8); - jit.bytePointer += 2; - - // Recursively processing the nested block - if (scanForBlockReturn(jit, newBytePointer - jit.bytePointer)) { - // Resetting bytePointer to an old value - jit.bytePointer = previousBytePointer; - return true; - } - - // Skipping block's bytecodes - jit.bytePointer = newBytePointer; - } - - if (instruction.high == opcode::doPrimitive) { - jit.bytePointer++; // skipping primitive number - continue; - } - - // We're now looking only for branch bytecodes - if (instruction.high != opcode::doSpecial) - continue; - - switch (instruction.low) { - case special::blockReturn: - // outs() << "Found a block return at offset " << currentOffset << "\n"; - - // Resetting bytePointer to an old value - jit.bytePointer = previousBytePointer; - return true; - - case special::branch: - case special::branchIfFalse: - case special::branchIfTrue: - //uint32_t targetOffset = byteCodes[jit.bytePointer] | (byteCodes[jit.bytePointer+1] << 8); - jit.bytePointer += 2; // skipping the branch offset data - continue; - } - } - - // Resetting bytePointer to an old value - jit.bytePointer = previousBytePointer; - return false; + return detector.isBlockReturnFound(); } void MethodCompiler::scanForBranches(TJITContext& jit, uint32_t byteCount /*= 0*/) From 52e60fda86e6fde24a1a180063f1a4d4e86104aa Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 25 Mar 2014 23:01:20 +0700 Subject: [PATCH 027/290] Refactors MethodCompiler::scanForBranches() using basic block visitor Issue: #32 --- src/MethodCompiler.cpp | 93 ++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 62 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 1ca9cc8..6494616 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -379,64 +379,31 @@ bool MethodCompiler::scanForBlockReturn(TJITContext& jit, uint32_t byteCount/* = void MethodCompiler::scanForBranches(TJITContext& jit, uint32_t byteCount /*= 0*/) { - // First analyzing pass. Scans the bytecode for the branch sites and - // collects branch targets. Creates target basic blocks beforehand. - // Target blocks are collected in the m_targetToBlockMap map with - // target bytecode offset as a key. - - uint32_t previousBytePointer = jit.bytePointer; - - TByteObject& byteCodes = * jit.method->byteCodes; - uint32_t stopPointer = jit.bytePointer + (byteCount ? byteCount : byteCodes.getSize()); - - // Processing the method's bytecodes - while (jit.bytePointer < stopPointer) { - // Decoding the pending instruction (TODO move to a function) - TInstruction instruction; - instruction.low = (instruction.high = byteCodes[jit.bytePointer++]) & 0x0F; - instruction.high >>= 4; - if (instruction.high == opcode::extended) { - instruction.high = instruction.low; - instruction.low = byteCodes[jit.bytePointer++]; - } - - if (instruction.high == opcode::pushBlock) { - // Skipping the nested block's bytecodes - uint16_t newBytePointer = byteCodes[jit.bytePointer] | (byteCodes[jit.bytePointer+1] << 8); - jit.bytePointer = newBytePointer; - continue; - } + // Iterating over method's basic blocks and creating their representation in LLVM + // Created blocks are collected in the m_targetToBlockMap map with bytecode offset as a key + + class Visitor : public st::BasicBlockVisitor { + public: + Visitor(TJITContext& jit) : BasicBlockVisitor(&jit.parsedMethod), m_jit(jit) { } + + private: + virtual bool visitBlock(st::BasicBlock& basicBlock) { + MethodCompiler* compiler = m_jit.compiler; // self reference + llvm::BasicBlock* newBlock = llvm::BasicBlock::Create( + compiler->m_JITModule->getContext(), // creating context + "branch.", // new block's name + m_jit.function // method's function + ); - if (instruction.high == opcode::doPrimitive) { - jit.bytePointer++; // skipping primitive number - continue; + compiler->m_targetToBlockMap[basicBlock.getOffset()] = newBlock; + return true; } - // We're now looking only for branch bytecodes - if (instruction.high != opcode::doSpecial) - continue; - - switch (instruction.low) { - case special::branch: - case special::branchIfTrue: - case special::branchIfFalse: { - // Loading branch target bytecode offset - uint32_t targetOffset = byteCodes[jit.bytePointer] | (byteCodes[jit.bytePointer+1] << 8); - jit.bytePointer += 2; // skipping the branch offset data - - if (m_targetToBlockMap.find(targetOffset) == m_targetToBlockMap.end()) { - // Creating the referred basic block and inserting it into the function - // Later it will be filled with instructions and linked to other blocks - BasicBlock* targetBasicBlock = BasicBlock::Create(m_JITModule->getContext(), "branch.", jit.function); - m_targetToBlockMap[targetOffset] = targetBasicBlock; - - } - } break; - } - } + TJITContext& m_jit; + }; - // Resetting bytePointer to an old value - jit.bytePointer = previousBytePointer; + Visitor visitor(jit); + visitor.run(); } Value* MethodCompiler::createArray(TJITContext& jit, uint32_t elementsCount) @@ -490,20 +457,22 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF if (jit.methodHasBlockReturn) writeLandingPad(jit); - // Switching builder context to the body's basic block from the preamble - BasicBlock* body = BasicBlock::Create(m_JITModule->getContext(), "body", jit.function); - jit.builder->SetInsertPoint(jit.preamble); - jit.builder->CreateBr(body); - - // Resetting the builder to the body - jit.builder->SetInsertPoint(body); - // Scans the bytecode for the branch sites and // collects branch targets. Creates target basic blocks beforehand. // Target blocks are collected in the m_targetToBlockMap map with // target bytecode offset as a key. scanForBranches(jit); + // Switching builder context to the first basic block from the preamble + BasicBlock* body = m_targetToBlockMap[0]; + body->setName("body."); + + jit.builder->SetInsertPoint(jit.preamble); + jit.builder->CreateBr(body); + + // Resetting the builder to the body + jit.builder->SetInsertPoint(body); + // Processing the method's bytecodes writeFunctionBody(jit); From ef981782da08249797f729ad34ba0b81dc0dbc6d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 26 Mar 2014 21:14:31 +0700 Subject: [PATCH 028/290] Refactors MethodCompiler's encode loop TSmalltalkInstruction and visitors Note: Processing of nested blocks should be refactored too. Issue: #32 --- include/jit.h | 9 ++- src/MethodCompiler.cpp | 168 ++++++++++++++++++++--------------------- src/vm.cpp | 3 - 3 files changed, 89 insertions(+), 91 deletions(-) diff --git a/include/jit.h b/include/jit.h index ea49567..cf634aa 100644 --- a/include/jit.h +++ b/include/jit.h @@ -221,12 +221,12 @@ class MethodCompiler { // used during the compilation process. struct TJITContext { st::ParsedMethod parsedMethod; // Parsed smalltalk method we're currently processing + st::TSmalltalkInstruction instruction; // currently processed instruction TMethod* method; // Smalltalk method we're currently processing - uint32_t bytePointer; + uint16_t bytePointer; llvm::Function* function; // LLVM function that is created based on method - TInstruction instruction; // currently processed instruction llvm::IRBuilder<>* builder; // Builder inserts instructions into basic blocks llvm::BasicBlock* preamble; @@ -253,7 +253,7 @@ class MethodCompiler { void pushValue(TStackValue* value); TJITContext(MethodCompiler* compiler, TMethod* method) - : parsedMethod(method), method(method), bytePointer(0), function(0), builder(0), + : parsedMethod(method), instruction(opcode::extended), method(method), bytePointer(0), function(0), builder(0), preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), contextHolder(0), selfHolder(0) { @@ -301,6 +301,7 @@ class MethodCompiler { void writePreamble(TJITContext& jit, bool isBlock = false); void writeFunctionBody(TJITContext& jit, uint32_t byteCount = 0); + void writeInstruction(TJITContext& jit); void writeLandingPad(TJITContext& jit); void doPushInstance(TJITContext& jit); @@ -308,7 +309,7 @@ class MethodCompiler { void doPushTemporary(TJITContext& jit); void doPushLiteral(TJITContext& jit); void doPushConstant(TJITContext& jit); - void doPushBlock(uint32_t currentOffset, TJITContext& jit); + void doPushBlock(TJITContext& jit); void doAssignTemporary(TJITContext& jit); void doAssignInstance(TJITContext& jit); void doMarkArguments(TJITContext& jit); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 6494616..b383f14 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -485,74 +485,79 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF void MethodCompiler::writeFunctionBody(TJITContext& jit, uint32_t byteCount /*= 0*/) { - TByteObject& byteCodes = * jit.method->byteCodes; - uint32_t stopPointer = jit.bytePointer + (byteCount ? byteCount : byteCodes.getSize()); + class Visitor : public st::InstructionVisitor { + public: + Visitor(TJITContext& jit) : InstructionVisitor(&jit.parsedMethod), m_jit(jit) { } - while (jit.bytePointer < stopPointer) { - uint32_t currentOffset = jit.bytePointer; + private: + virtual bool visitBlock(st::BasicBlock& basicBlock) { + // A new basic block had just been started + // Switching context to it and linking blocks if necessary - if (m_targetToBlockMap.find(currentOffset) != m_targetToBlockMap.end()) { - // Somewhere in the code we have a branch instruction that - // points to the current offset. We need to end the current - // basic block and start a new one, linking previous - // basic block to a new one. + llvm::BasicBlock* newBlock = m_jit.compiler->m_targetToBlockMap[basicBlock.getOffset()]; - BasicBlock* newBlock = m_targetToBlockMap.find(currentOffset)->second; // Picking a basic block //If the current BB does not have a terminator, we create a br to newBlock - if (! jit.builder->GetInsertBlock()->getTerminator() ) { - jit.builder->CreateBr(newBlock); // Linking current block to a new one - // Updating the block referers + if (! m_jit.builder->GetInsertBlock()->getTerminator() ) { + m_jit.builder->CreateBr(newBlock); // Linking current block to a new one // Inserting current block as a referer to the newly created one // Popping the value may result in popping the referer's stack // or even generation of phi function if there are several referers - jit.basicBlockContexts[newBlock].referers.insert(jit.builder->GetInsertBlock()); + m_jit.basicBlockContexts[newBlock].referers.insert(m_jit.builder->GetInsertBlock()); } - newBlock->moveAfter(jit.builder->GetInsertBlock()); //for a pretty sequenced BB output - jit.builder->SetInsertPoint(newBlock); // and switching builder to a new block - } + newBlock->moveAfter(m_jit.builder->GetInsertBlock()); // for a pretty sequenced BB output + m_jit.builder->SetInsertPoint(newBlock); - // First of all decoding the pending instruction - jit.instruction.low = (jit.instruction.high = byteCodes[jit.bytePointer++]) & 0x0F; - jit.instruction.high >>= 4; - if (jit.instruction.high == opcode::extended) { - jit.instruction.high = jit.instruction.low; - jit.instruction.low = byteCodes[jit.bytePointer++]; + return InstructionVisitor::visitBlock(basicBlock); } - // Then writing the code - switch (jit.instruction.high) { - // TODO Boundary checks against container's real size - case opcode::pushInstance: doPushInstance(jit); break; - case opcode::pushArgument: doPushArgument(jit); break; - case opcode::pushTemporary: doPushTemporary(jit); break; - case opcode::pushLiteral: doPushLiteral(jit); break; - case opcode::pushConstant: doPushConstant(jit); break; - - case opcode::pushBlock: doPushBlock(currentOffset, jit); break; - - case opcode::assignTemporary: doAssignTemporary(jit); break; - case opcode::assignInstance: doAssignInstance(jit); break; - - case opcode::markArguments: doMarkArguments(jit); break; - case opcode::sendUnary: doSendUnary(jit); break; - case opcode::sendBinary: doSendBinary(jit); break; - case opcode::sendMessage: doSendMessage(jit); break; - - case opcode::doSpecial: doSpecial(jit); break; - case opcode::doPrimitive: doPrimitive(jit); break; - - // Treating opcode 0 as NOP command - // This is a temporary hack to solve bug #26 - // It is triggered by malformed method code - // compiled by in-image soft compiler - case 0: break; - - default: - std::fprintf(stderr, "JIT: Invalid opcode %d at offset %d in method %s\n", - jit.instruction.high, jit.bytePointer, jit.method->name->toString().c_str()); + virtual bool visitInstruction(const st::TSmalltalkInstruction& instruction) { + // Processing instruction + m_jit.instruction = instruction; + m_jit.compiler->writeInstruction(m_jit); + + return true; } + + TJITContext& m_jit; + }; + + Visitor visitor(jit); + visitor.run(); +} + +void MethodCompiler::writeInstruction(TJITContext& jit) { + switch (jit.instruction.getOpcode()) { + // TODO Boundary checks against container's real size + case opcode::pushInstance: doPushInstance(jit); break; + case opcode::pushArgument: doPushArgument(jit); break; + case opcode::pushTemporary: doPushTemporary(jit); break; + case opcode::pushLiteral: doPushLiteral(jit); break; + case opcode::pushConstant: doPushConstant(jit); break; + + case opcode::pushBlock: doPushBlock(jit); break; + + case opcode::assignTemporary: doAssignTemporary(jit); break; + case opcode::assignInstance: doAssignInstance(jit); break; + + case opcode::markArguments: doMarkArguments(jit); break; + case opcode::sendUnary: doSendUnary(jit); break; + case opcode::sendBinary: doSendBinary(jit); break; + case opcode::sendMessage: doSendMessage(jit); break; + + case opcode::doSpecial: doSpecial(jit); break; + case opcode::doPrimitive: doPrimitive(jit); break; + + // Treating opcode 0 as NOP command + // This is a temporary hack to solve bug #26 + // It is triggered by malformed method code + // compiled by in-image soft compiler + case 0: break; + + default: + std::fprintf(stderr, "JIT: Invalid opcode %d at offset %d in method %s\n", + jit.instruction.getOpcode(), jit.bytePointer, jit.method->name->toString().c_str()); } } @@ -593,31 +598,31 @@ void MethodCompiler::doPushInstance(TJITContext& jit) // Self is interpreted as object array. // Array elements are instance variables - uint8_t index = jit.instruction.low; + uint8_t index = jit.instruction.getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadInstance, index)); } void MethodCompiler::doPushArgument(TJITContext& jit) { - uint8_t index = jit.instruction.low; + uint8_t index = jit.instruction.getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadArgument, index)); } void MethodCompiler::doPushTemporary(TJITContext& jit) { - uint8_t index = jit.instruction.low; + uint8_t index = jit.instruction.getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadTemporary, index)); } void MethodCompiler::doPushLiteral(TJITContext& jit) { - uint8_t index = jit.instruction.low; + uint8_t index = jit.instruction.getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadLiteral, index)); } void MethodCompiler::doPushConstant(TJITContext& jit) { - const uint32_t constant = jit.instruction.low; + const uint32_t constant = jit.instruction.getArgument(); Value* constantValue = 0; switch (constant) { @@ -650,11 +655,9 @@ void MethodCompiler::doPushConstant(TJITContext& jit) jit.pushValue(constantValue); } -void MethodCompiler::doPushBlock(uint32_t currentOffset, TJITContext& jit) +void MethodCompiler::doPushBlock(TJITContext& jit) { - TByteObject& byteCodes = * jit.method->byteCodes; - uint16_t newBytePointer = byteCodes[jit.bytePointer] | (byteCodes[jit.bytePointer+1] << 8); - jit.bytePointer += 2; + const uint16_t newBytePointer = jit.instruction.getExtra(); TJITContext blockContext(this, jit.method); blockContext.bytePointer = jit.bytePointer; @@ -662,7 +665,7 @@ void MethodCompiler::doPushBlock(uint32_t currentOffset, TJITContext& jit) // Creating block function named Class>>method@offset const uint16_t blockOffset = jit.bytePointer; std::ostringstream ss; - ss << jit.method->klass->name->toString() + ">>" + jit.method->name->toString() << "@" << blockOffset; //currentOffset; + ss << jit.method->klass->name->toString() + ">>" + jit.method->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); std::vector blockParams; @@ -700,7 +703,7 @@ void MethodCompiler::doPushBlock(uint32_t currentOffset, TJITContext& jit) // Create block object and fill it with context information Value* args[] = { jit.getCurrentContext(), // creatingContext - jit.builder->getInt8(jit.instruction.low), // arg offset + jit.builder->getInt8(jit.instruction.getArgument()), // arg offset jit.builder->getInt16(blockOffset) // bytePointer }; Value* blockObject = jit.builder->CreateCall(m_runtimeAPI.createBlock, args); @@ -714,7 +717,7 @@ void MethodCompiler::doPushBlock(uint32_t currentOffset, TJITContext& jit) void MethodCompiler::doAssignTemporary(TJITContext& jit) { - uint8_t index = jit.instruction.low; + uint8_t index = jit.instruction.getArgument(); Value* value = jit.lastValue(); IRBuilder<>& builder = * jit.builder; @@ -726,7 +729,7 @@ void MethodCompiler::doAssignTemporary(TJITContext& jit) void MethodCompiler::doAssignInstance(TJITContext& jit) { - uint8_t index = jit.instruction.low; + uint8_t index = jit.instruction.getArgument(); Value* value = jit.lastValue(); IRBuilder<>& builder = * jit.builder; @@ -741,7 +744,7 @@ void MethodCompiler::doAssignInstance(TJITContext& jit) void MethodCompiler::doMarkArguments(TJITContext& jit) { // Here we need to create the arguments array from the values on the stack - uint8_t argumentsCount = jit.instruction.low; + uint8_t argumentsCount = jit.instruction.getArgument(); // FIXME Probably we may unroll the arguments array and pass the values directly. // However, in some cases this may lead to additional architectural problems. @@ -765,12 +768,12 @@ void MethodCompiler::doSendUnary(TJITContext& jit) Value* value = jit.popValue(); Value* condition = 0; - switch ( static_cast(jit.instruction.low) ) { + switch ( static_cast(jit.instruction.getArgument()) ) { case unaryBuiltIns::isNil: condition = jit.builder->CreateICmpEQ(value, m_globals.nilObject, "isNil."); break; case unaryBuiltIns::notNil: condition = jit.builder->CreateICmpNE(value, m_globals.nilObject, "notNil."); break; default: - std::fprintf(stderr, "JIT: Invalid opcode %d passed to sendUnary\n", jit.instruction.low); + std::fprintf(stderr, "JIT: Invalid opcode %d passed to sendUnary\n", jit.instruction.getArgument()); } Value* result = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); @@ -780,7 +783,7 @@ void MethodCompiler::doSendUnary(TJITContext& jit) void MethodCompiler::doSendBinary(TJITContext& jit) { // 0, 1 or 2 for '<', '<=' or '+' respectively - binaryBuiltIns::Operator opcode = static_cast(jit.instruction.low); + binaryBuiltIns::Operator opcode = static_cast(jit.instruction.getArgument()); Value* rightValue = jit.popValue(); Value* leftValue = jit.popValue(); @@ -856,7 +859,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) Value* argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); Value* sendMessageArgs[] = { jit.getCurrentContext(), // calling context - m_globals.binarySelectors[jit.instruction.low], + m_globals.binarySelectors[jit.instruction.getArgument()], argumentsArray, // default receiver class @@ -897,11 +900,11 @@ void MethodCompiler::doSendMessage(TJITContext& jit) Value* arguments = jit.popValue(); // First of all we need to get the actual message selector - Value* selectorObject = jit.getLiteral(jit.instruction.low); + Value* selectorObject = jit.getLiteral(jit.instruction.getArgument()); Value* messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); std::ostringstream ss; - ss << "#" << jit.method->literals->getField(jit.instruction.low)->toString() << "."; + ss << "#" << jit.method->literals->getField(jit.instruction.getArgument())->toString() << "."; messageSelector->setName(ss.str()); // Forming a message parameters @@ -940,8 +943,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) void MethodCompiler::doSpecial(TJITContext& jit) { - TByteObject& byteCodes = * jit.method->byteCodes; - uint8_t opcode = jit.instruction.low; + const uint8_t opcode = jit.instruction.getArgument(); BasicBlock::iterator iPreviousInst = jit.builder->GetInsertPoint(); if (iPreviousInst != jit.builder->GetInsertBlock()->begin()) @@ -1001,8 +1003,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::branch: { // Loading branch target bytecode offset - uint32_t targetOffset = byteCodes[jit.bytePointer] | (byteCodes[jit.bytePointer+1] << 8); - jit.bytePointer += 2; // skipping the branch offset data + uint32_t targetOffset = jit.instruction.getExtra(); if (!iPreviousInst->isTerminator()) { // Finding appropriate branch target @@ -1018,8 +1019,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::branchIfTrue: case special::branchIfFalse: { // Loading branch target bytecode offset - uint32_t targetOffset = byteCodes[jit.bytePointer] | (byteCodes[jit.bytePointer+1] << 8); - jit.bytePointer += 2; // skipping the branch offset data + uint32_t targetOffset = jit.instruction.getExtra(); if (!iPreviousInst->isTerminator()) { // Finding appropriate branch target @@ -1049,7 +1049,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) Value* argsObject = jit.popValue(); Value* arguments = jit.builder->CreateBitCast(argsObject, m_baseTypes.objectArray->getPointerTo()); - uint32_t literalIndex = byteCodes[jit.bytePointer++]; + uint32_t literalIndex = jit.instruction.getExtra(); Value* selectorObject = jit.getLiteral(literalIndex); Value* messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); @@ -1078,7 +1078,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) void MethodCompiler::doPrimitive(TJITContext& jit) { - uint32_t opcode = jit.method->byteCodes->getByte(jit.bytePointer++); + uint32_t opcode = jit.instruction.getArgument(); Value* primitiveResult = 0; Value* primitiveFailed = jit.builder->getFalse(); @@ -1243,7 +1243,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, Value* object = jit.popValue(); Value* block = jit.builder->CreateBitCast(object, m_baseTypes.block->getPointerTo()); - int32_t argCount = jit.instruction.low - 1; + int32_t argCount = jit.instruction.getArgument() - 1; Value* blockAsContext = jit.builder->CreateBitCast(block, m_baseTypes.context->getPointerTo()); Function* getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); @@ -1459,7 +1459,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, default: { // Here we need to create the arguments array from the values on the stack - uint8_t argumentsCount = jit.instruction.low; + uint8_t argumentsCount = jit.instruction.getArgument(); Value* argumentsObject = createArray(jit, argumentsCount); // Filling object with contents diff --git a/src/vm.cpp b/src/vm.cpp index 90c5bf0..3b2368c 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -305,8 +305,6 @@ SmalltalkVM::TExecuteResult SmalltalkVM::execute(TProcess* p, uint32_t ticks) void SmalltalkVM::doPushBlock(TVMExecutionContext& ec) { - TByteObject& byteCodes = * ec.currentContext->method->byteCodes; - // Block objects are usually inlined in the wrapping method code // pushBlock operation creates a block object initialized // with the proper bytecode, stack, arguments and the wrapping context. @@ -536,7 +534,6 @@ void SmalltalkVM::doSendBinary(TVMExecutionContext& ec) SmalltalkVM::TExecuteResult SmalltalkVM::doSpecial(hptr& process, TVMExecutionContext& ec) { - TByteObject& byteCodes = * ec.currentContext->method->byteCodes; TObjectArray& arguments = * ec.currentContext->arguments; TSymbolArray& literals = * ec.currentContext->method->literals; From 68e85de69373738f04a6b16756ed1a2629401f97 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 22 Apr 2014 01:14:48 +0400 Subject: [PATCH 029/290] Fixes the usage of 'argument' and 'extra' for opcode::doPrimitive Issue: #32 --- src/InstructionDecoder.cpp | 8 +++----- src/vm.cpp | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/InstructionDecoder.cpp b/src/InstructionDecoder.cpp index 50b31d0..55f3b17 100644 --- a/src/InstructionDecoder.cpp +++ b/src/InstructionDecoder.cpp @@ -32,11 +32,9 @@ const TSmalltalkInstruction InstructionDecoder::decodeAndShiftPointer(const TByt break; case opcode::doPrimitive: - // Primitive number do not fit into lower 4 bits of opcode byte. - // So it is stored in a separate byte right after. Technically, - // this value is an argument for instruction so it would be logical - // to hold it in the argument field. - argument = byteCodes[bytePointer++]; + // The index number of the primitive number does not fit 4 lower bits of the opcode. + // So it is stored in a separate byte right after 'argument'. + extra = byteCodes[bytePointer++]; break; case opcode::doSpecial: diff --git a/src/vm.cpp b/src/vm.cpp index 3b2368c..4aeabe7 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -317,13 +317,13 @@ void SmalltalkVM::doPushBlock(TVMExecutionContext& ec) // such as sendMessage to a receiver with our block as a parameter or something similar. // New byte pointer that points to the code right after the inline block - uint16_t newBytePointer = ec.instruction.getExtra(); + const uint16_t newBytePointer = ec.instruction.getExtra(); // Creating block object hptr newBlock = newObject(); // Allocating block's stack - uint32_t stackSize = ec.currentContext->method->stackSize; + const uint32_t stackSize = ec.currentContext->method->stackSize; newBlock->stack = newObject(stackSize/*, false*/); newBlock->argumentLocation = ec.instruction.getArgument(); @@ -653,7 +653,7 @@ void SmalltalkVM::doPushConstant(TVMExecutionContext& ec) SmalltalkVM::TExecuteResult SmalltalkVM::doPrimitive(hptr& process, TVMExecutionContext& ec) { - uint8_t opcode = ec.instruction.getArgument(); + uint8_t opcode = ec.instruction.getExtra(); // First of all, executing the primitive // If primitive succeeds then stop execution of the current method From 0bf87ae0b6ef02c1701258170a46a4be79889e8d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 1 May 2014 22:06:50 +0700 Subject: [PATCH 030/290] Fixes type of argument index Issue: #32 --- include/analysis.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 759484a..94dcc25 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -136,7 +136,7 @@ class InstructionNode : public ControlNode { m_arguments[index] = value; } - uint32_t addArgument(ControlNode* value) { + std::size_t addArgument(ControlNode* value) { m_arguments.push_back(value); return m_arguments.size() - 1; } @@ -213,7 +213,7 @@ class ControlDomain { return value; } - void requestArgument(uint32_t index, InstructionNode* forNode) { + void requestArgument(std::size_t index, InstructionNode* forNode) { if (! m_localStack.empty()) { ControlNode* argument = popValue(); argument->addEdge(forNode); @@ -224,7 +224,7 @@ class ControlDomain { } struct TArgumentRequest { - uint32_t index; + std::size_t index; InstructionNode* requestingNode; }; typedef std::vector TRequestList; From e9a40d42c302612c7ef513b8e6a54df608d053e4 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 May 2014 13:45:12 +0700 Subject: [PATCH 031/290] Fixes TSmalltalkInstruction::serialize() Issue: #32 --- include/instructions.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/instructions.h b/include/instructions.h index dda8c94..11108f4 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -35,7 +35,10 @@ struct TSmalltalkInstruction { // Return fixed width representation of bytecode suitable for storing in arrays TUnpackedBytecode serialize() const { - return static_cast(m_opcode) | (m_argument << 8) | (m_extra << 16); + return + static_cast(m_opcode) | + static_cast(m_argument) << 8 | + static_cast(m_extra) << 16; } bool isTerminator() const { From d9ac4a860570fae208e5988059315db9a8be7b05 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 May 2014 13:45:50 +0700 Subject: [PATCH 032/290] Fixes indentation in InstructionDecoder Issue: #32 --- src/InstructionDecoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/InstructionDecoder.cpp b/src/InstructionDecoder.cpp index 55f3b17..e38f1a5 100644 --- a/src/InstructionDecoder.cpp +++ b/src/InstructionDecoder.cpp @@ -53,8 +53,8 @@ const TSmalltalkInstruction InstructionDecoder::decodeAndShiftPointer(const TByt } break; - default: // Nothing to do here - break; + default: // Nothing to do here + break; } return TSmalltalkInstruction(opcode, argument, extra); From db74ae5215b4656a4d3b743d8e623b9cd7c08bf1 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 May 2014 14:02:07 +0700 Subject: [PATCH 033/290] Adds stub instruction skipping logic Issue: #32 --- src/ControlGraph.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index fbf6c5e..053d8b8 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -13,15 +13,20 @@ template<> TauNode* ControlGraph::newNode() { return static_castgetParsedMethod()), m_graph(graph) { } + GraphConstructor(ControlGraph* graph) + : InstructionVisitor(graph->getParsedMethod()), m_graph(graph), m_skipStubInstructions(false) { } virtual bool visitBlock(BasicBlock& basicBlock) { m_currentDomain = m_graph->getDomainFor(&basicBlock); m_currentDomain->setBasicBlock(&basicBlock); + m_skipStubInstructions = false; return InstructionVisitor::visitBlock(basicBlock); } virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { + if (m_skipStubInstructions) + return true; + // Initializing instruction node InstructionNode* newNode = m_graph->newNode(); newNode->setInstruction(instruction); @@ -41,6 +46,7 @@ class GraphConstructor : public InstructionVisitor { ControlGraph* m_graph; ControlDomain* m_currentDomain; + bool m_skipStubInstructions; }; void GraphConstructor::processNode(InstructionNode* node) @@ -117,6 +123,11 @@ void GraphConstructor::processSpecials(InstructionNode* node) case special::branch: assert(! m_currentDomain->getTerminator()); m_currentDomain->setTerminator(node); + + // All instructions that go after terminator within current block + // are stubs that were added by the image builder. Control flow + // will never reach such instructions, so they may be ignored safely. + m_skipStubInstructions = true; break; } } From 20d3f6ef91c3e94392907419d18d137cd4a743d7 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 May 2014 14:36:00 +0700 Subject: [PATCH 034/290] Fixes BasicBlock::getTerminator() Issue: #32 --- include/instructions.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/instructions.h b/include/instructions.h index 11108f4..06c5a4f 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -169,6 +169,9 @@ class BasicBlock { TBasicBlockSet& getReferers() { return m_referers; } bool getTerminator(TSmalltalkInstruction& out) const { + if (m_instructions.empty()) + return false; + TSmalltalkInstruction result(m_instructions.back()); if (result.isTerminator()) { out = result; From 2a069c56fec6214375700c3d3d27958e72bacc37 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 May 2014 14:37:10 +0700 Subject: [PATCH 035/290] Fixes ParsedBytecode::parse() Issue: #32 --- src/ParsedBytecode.cpp | 76 ++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index dd70db1..df431eb 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -66,46 +66,48 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { decoder.setBytePointer(startOffset); while (decoder.getBytePointer() < stopPointer) { - // Switching basic block if current offset is a branch target - const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(decoder.getBytePointer()); - if (iBlock != m_offsetToBasicBlock.end()) { - BasicBlock* const nextBlock = iBlock->second; - - // Checking if previous block referred current block - // by jumping into it and adding reference if needed - TSmalltalkInstruction terminator(opcode::extended); - if (currentBasicBlock->getTerminator(terminator)) { - if (terminator.isBranch()) { - if (terminator.getExtra() == decoder.getBytePointer()) { - // Unconditional branch case - assert(terminator.getArgument() == special::branch); - nextBlock->getReferers().insert(currentBasicBlock); - } else { - // Previous block referred some other block instead. - // Terminator is one of conditional branch instructions. - // We need to refer both of branch targets here. - assert(terminator.getArgument() == special::branchIfTrue - || terminator.getArgument() == special::branchIfFalse); - - // Case when branch condition is not met - nextBlock->getReferers().insert(currentBasicBlock); - - // Case when branch condition is met - const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); - if (iTargetBlock != m_offsetToBasicBlock.end()) - iTargetBlock->second->getReferers().insert(currentBasicBlock); - else - assert(false); + if (decoder.getBytePointer()) { + // Switching basic block if current offset is a branch target + const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(decoder.getBytePointer()); + if (iBlock != m_offsetToBasicBlock.end()) { + BasicBlock* const nextBlock = iBlock->second; + + // Checking if previous block referred current block + // by jumping into it and adding reference if needed + TSmalltalkInstruction terminator(opcode::extended); + if (currentBasicBlock->getTerminator(terminator)) { + if (terminator.isBranch()) { + if (terminator.getExtra() == decoder.getBytePointer()) { + // Unconditional branch case + assert(terminator.getArgument() == special::branch); + nextBlock->getReferers().insert(currentBasicBlock); + } else { + // Previous block referred some other block instead. + // Terminator is one of conditional branch instructions. + // We need to refer both of branch targets here. + assert(terminator.getArgument() == special::branchIfTrue + || terminator.getArgument() == special::branchIfFalse); + + // Case when branch condition is not met + nextBlock->getReferers().insert(currentBasicBlock); + + // Case when branch condition is met + const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); + if (iTargetBlock != m_offsetToBasicBlock.end()) + iTargetBlock->second->getReferers().insert(currentBasicBlock); + else + assert(false); + } } + } else { + // Adding branch instruction to link blocks + currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, decoder.getBytePointer())); + nextBlock->getReferers().insert(currentBasicBlock); } - } else { - // Adding branch instruction to link blocks - currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, decoder.getBytePointer())); - nextBlock->getReferers().insert(currentBasicBlock); - } - // Switching to a new block - currentBasicBlock = nextBlock; + // Switching to a new block + currentBasicBlock = nextBlock; + } } // Fetching instruction and appending it to the current basic block From 23c47db50af2b87d515456b3e1d86548a4ac2e77 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 May 2014 18:47:18 +0700 Subject: [PATCH 036/290] Fixes TSmalltalkInstruction deserializing constructor Issue: #32 --- include/instructions.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index 06c5a4f..d97356f 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -25,8 +25,8 @@ struct TSmalltalkInstruction { // Initialize instruction from the unpacked value TSmalltalkInstruction(TUnpackedBytecode bytecode) { m_opcode = static_cast(bytecode & 0xFF); - m_argument = static_cast((bytecode >> 8) & 0xFF); - m_extra = static_cast((bytecode >> 16) & 0xFF); + m_argument = static_cast(bytecode >> 8); + m_extra = static_cast(bytecode >> 16); } TOpcode getOpcode() const { return m_opcode; } From ea8cf7b9c3c5c58c18adf91a72fc8cc4564747d5 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 May 2014 18:53:35 +0700 Subject: [PATCH 037/290] Fixes TSmalltalkInstruction::isBranch() Issue: #32 --- include/instructions.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index d97356f..ad2e0f7 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -64,9 +64,9 @@ struct TSmalltalkInstruction { return false; switch (m_argument) { - case special::stackReturn: - case special::selfReturn: - case special::blockReturn: + case special::branch: + case special::branchIfFalse: + case special::branchIfTrue: return true; default: From e69c7e435059290e5aa02d579e055bd9840e9c57 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 4 May 2014 20:18:34 +0700 Subject: [PATCH 038/290] Adds dynamic cast for ControlNode Issue: #32 --- include/analysis.h | 8 ++++++++ src/ControlGraph.cpp | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/include/analysis.h b/include/analysis.h index 94dcc25..a4c9653 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -90,6 +90,10 @@ class ControlNode { virtual ~ControlNode() { } virtual TNodeType getNodeType() const = 0; + // Dynamically cast node to a specified type. + // If type does not match null is returned. + template T* cast(); + uint32_t getIndex() const { return m_index; } ControlDomain* getDomain() const { return m_domain; } @@ -320,6 +324,10 @@ class ControlGraph { TDomainMap m_blocksToDomains; }; +template<> InstructionNode* ControlNode::cast(); +template<> PhiNode* ControlNode::cast(); +template<> TauNode* ControlNode::cast(); + template<> InstructionNode* ControlGraph::newNode(); template<> PhiNode* ControlGraph::newNode(); template<> TauNode* ControlGraph::newNode(); diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 053d8b8..f848dc2 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -7,6 +7,10 @@ bool NodeIndexCompare::operator() (const ControlNode* a, const ControlNode* b) return a->getIndex() < b->getIndex(); } +template<> InstructionNode* ControlNode::cast() { return this->getNodeType() == ntInstruction ? static_cast(this) : 0; } +template<> PhiNode* ControlNode::cast() { return this->getNodeType() == ntPhi ? static_cast(this) : 0; } +template<> TauNode* ControlNode::cast() { return this->getNodeType() == ntTau ? static_cast(this) : 0; } + template<> InstructionNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntInstruction)); } template<> PhiNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntPhi)); } template<> TauNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntTau)); } From 9d9d8f2f970f1cbbfc6e3a0b5ffa9bef1d33a839 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 4 May 2014 20:21:47 +0700 Subject: [PATCH 039/290] Adds basic control graph visualization using OGDF library Note: Will not compile if OGDF is not found in llst/ogdf/ Issue: #32 --- CMakeLists.txt | 5 +- include/visualization.h | 32 ++++++++++ src/ControlGraphVisualizer.cpp | 104 +++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 include/visualization.h create mode 100644 src/ControlGraphVisualizer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 203b3b6..e0707e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ set (CMAKE_CXX_FLAGS_RELEASE "-O3") add_subdirectory(src) add_subdirectory(include) include_directories(include) +include_directories(ogdf) # Base set of sources needed in every build set(CPP_FILES @@ -73,6 +74,7 @@ set(CPP_FILES src/ParsedBlock.cpp src/ControlGraph.cpp + src/ControlGraphVisualizer.cpp ) if (LLVM) @@ -87,7 +89,8 @@ if (LLVM) endif() add_executable(llst ${CPP_FILES}) -target_link_libraries(llst ${LLVM_LIBS} ${READLINE_LIB} ${LLVM_LD_FLAGS}) +set_target_properties(llst PROPERTIES LINK_FLAGS "-L../ogdf/_release") +target_link_libraries(llst ${LLVM_LIBS} ${READLINE_LIB} ${LLVM_LD_FLAGS} OGDF) if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) # this is a 64-bit OS diff --git a/include/visualization.h b/include/visualization.h new file mode 100644 index 0000000..61ebf29 --- /dev/null +++ b/include/visualization.h @@ -0,0 +1,32 @@ +#ifndef LLST_VISUALIZATION_H_INCLUDED +#define LLST_VISUALIZATION_H_INCLUDED + +#include +#include + +#include <../ogdf/ogdf/basic/Graph.h> +#include <../ogdf/ogdf/basic/GraphAttributes.h> + +class ControlGraphVisualizer : public st::NodeVisitor { +public: + ControlGraphVisualizer(st::ControlGraph* graph) : st::NodeVisitor(graph) { m_ogdfGraph = new ogdf::Graph(); } + virtual ~ControlGraphVisualizer() { delete m_ogdfGraph; } + + virtual bool visitDomain(st::ControlDomain& domain); + virtual bool visitNode(st::ControlNode& node); + + void writeGraphTo(const std::string& fileName); + +private: + ogdf::NodeElement* getNodeFor(st::ControlNode*); + void addEdge(st::ControlNode* from, st::ControlNode* to); + +private: + ogdf::Graph* m_ogdfGraph; + ogdf::GraphAttributes* m_ogdfAttributes; + + typedef std::map TNodeMap; + TNodeMap m_nodes; +}; + +#endif diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp new file mode 100644 index 0000000..bbb5765 --- /dev/null +++ b/src/ControlGraphVisualizer.cpp @@ -0,0 +1,104 @@ + +#include + +bool ControlGraphVisualizer::visitDomain(st::ControlDomain& domain) { + // TODO Create domain frame + + return st::NodeVisitor::visitDomain(domain); +} + +bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { + ogdf::NodeElement* element = getNodeFor(&node); + + const st::TNodeSet& inEdges = node.getInEdges(); + const st::TNodeSet& outEdges = node.getOutEdges(); + + // Processing incoming edges + st::TNodeSet::iterator iEdge = inEdges.begin(); + for (; iEdge != inEdges.end(); ++iEdge) + addEdge(*iEdge, &node); + + // Processing outgoing edges + iEdge = outEdges.begin(); + for (; iEdge != outEdges.end(); ++iEdge) + addEdge(&node, *iEdge); + + // Processing argument edges + if (const st::InstructionNode* instruction = node.cast()) { + for (std::size_t index = 0; index < instruction->getArgumentsCount(); index++) { + ogdf::NodeElement* argument = getNodeFor(instruction->getArgument(index)); + ogdf::EdgeElement* edge = m_ogdfGraph->newEdge(element, argument); + + m_ogdfAttributes->labelEdge(edge) = index; + m_ogdfAttributes->colorEdge(edge) = "blue"; + m_ogdfAttributes->arrowEdge(edge) = ogdf::GraphAttributes::first; + } + } + + return st::NodeVisitor::visitNode(node); +} + +ogdf::NodeElement* ControlGraphVisualizer::getNodeFor(st::ControlNode* node) { + TNodeMap::iterator iNode = m_nodes.find(node); + + if (iNode != m_nodes.end()) + return iNode->second; + + ogdf::NodeElement* element = m_ogdfGraph->newNode(node->getIndex()); + m_ogdfAttributes->shapeNode(element) = ogdf::GraphAttributes::oval; + m_ogdfAttributes->width(element) = 32; + m_ogdfAttributes->height(element) = 32; + + ogdf::String& label = m_ogdfAttributes->labelNode(element); + + switch (node->getNodeType()) { + case st::ControlNode::ntPhi: + label = "Phi"; + m_ogdfAttributes->colorNode(element) = "grey"; + break; + + case st::ControlNode::ntTau: + label = "Tau"; + m_ogdfAttributes->colorNode(element) = "green"; + break; + + case st::ControlNode::ntInstruction: + //label = node.cast()->getInstruction().toString(); + m_ogdfAttributes->colorNode(element) = "blue"; + + default: + label = node->getIndex(); + } + + m_nodes[node] = element; + return element; +} + +void ControlGraphVisualizer::addEdge(st::ControlNode* from, st::ControlNode* to) { + ogdf::NodeElement* fromElement = getNodeFor(from); + ogdf::NodeElement* toElement = getNodeFor(to); + + ogdf::EdgeElement* edge = m_ogdfGraph->searchEdge(fromElement, toElement); + if (edge) // Skipping if edge already exists + return; + + edge = m_ogdfGraph->newEdge(fromElement, toElement); + m_ogdfAttributes->arrowEdge(edge) = ogdf::GraphAttributes::first; + + const st::InstructionNode* fromInstruction = from->cast(); + const st::InstructionNode* toInstruction = to->cast(); + + if ((fromInstruction && fromInstruction->getInstruction().isBranch()) || + (toInstruction && toInstruction->getArgumentsCount() == 0)) + { + m_ogdfAttributes->colorEdge(edge) = "grey"; + m_ogdfAttributes->styleEdge(edge) = ogdf::GraphAttributes::esDash; + } + + if (to->getNodeType() == st::ControlNode::ntPhi) + m_ogdfAttributes->colorEdge(edge) = "grey"; +} + +void ControlGraphVisualizer::writeGraphTo(const std::string& fileName) { + m_ogdfGraph->writeGML(fileName.c_str()); +} From 83a2720fbacf36bc30a3fcff615bf97ffa47cd23 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 10 May 2014 14:43:43 +0700 Subject: [PATCH 040/290] Adds const version of TObject::cast() Issue: #32 --- include/types.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/types.h b/include/types.h index 909f726..27b40db 100644 --- a/include/types.h +++ b/include/types.h @@ -159,7 +159,8 @@ struct TObject { TObject*& operator [] (uint32_t index) { return fields[index]; } void putField(uint32_t index, TObject* value) { fields[index] = value; } - template T* cast() const { return static_cast(this); } + template T* cast() { return static_cast(this); } + template const T* cast() const { return static_cast(this); } // Helper constant for template instantination enum { InstancesAreBinary = false }; From b0ce97a3d05f01a3d2e9be489f7bb5065bfa76e6 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 10 May 2014 14:45:51 +0700 Subject: [PATCH 041/290] Adds simple regular layout to graph visualization Issue: #32 --- include/visualization.h | 9 +++- src/ControlGraphVisualizer.cpp | 94 +++++++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/include/visualization.h b/include/visualization.h index 61ebf29..cfc883b 100644 --- a/include/visualization.h +++ b/include/visualization.h @@ -4,8 +4,8 @@ #include #include -#include <../ogdf/ogdf/basic/Graph.h> -#include <../ogdf/ogdf/basic/GraphAttributes.h> +#include +#include class ControlGraphVisualizer : public st::NodeVisitor { public: @@ -20,6 +20,8 @@ class ControlGraphVisualizer : public st::NodeVisitor { private: ogdf::NodeElement* getNodeFor(st::ControlNode*); void addEdge(st::ControlNode* from, st::ControlNode* to); + void applyLayout(); + void setNodeProperties(st::ControlNode* node, ogdf::NodeElement* element); private: ogdf::Graph* m_ogdfGraph; @@ -27,6 +29,9 @@ class ControlGraphVisualizer : public st::NodeVisitor { typedef std::map TNodeMap; TNodeMap m_nodes; + + uint32_t m_lastX; + uint32_t m_lastY; }; #endif diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index bbb5765..a0f472b 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -1,6 +1,11 @@ #include +#include +#include +#include +#include + bool ControlGraphVisualizer::visitDomain(st::ControlDomain& domain) { // TODO Create domain frame @@ -38,37 +43,64 @@ bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { return st::NodeVisitor::visitNode(node); } -ogdf::NodeElement* ControlGraphVisualizer::getNodeFor(st::ControlNode* node) { - TNodeMap::iterator iNode = m_nodes.find(node); - - if (iNode != m_nodes.end()) - return iNode->second; - - ogdf::NodeElement* element = m_ogdfGraph->newNode(node->getIndex()); +void ControlGraphVisualizer::setNodeProperties(st::ControlNode* node, ogdf::NodeElement* element) { + // Setting basic attributes m_ogdfAttributes->shapeNode(element) = ogdf::GraphAttributes::oval; m_ogdfAttributes->width(element) = 32; m_ogdfAttributes->height(element) = 32; - ogdf::String& label = m_ogdfAttributes->labelNode(element); + // Calculating node position + uint32_t newX = 0; + uint32_t newY = 0; + + // We do not want to apply complex layout because our graph is pretty straighforward. + // So placing nodes in natural order but stacking argument nodes where possible. + if (node->getInEdges().size()) { + // Shifting to the next colon + newX = m_lastX + 64; + newY = 0; + } else { + // Stacking it with previous one. + newX = m_lastX; + newY = m_lastY + 64; + } + m_ogdfAttributes->x(element) = newX; + m_ogdfAttributes->y(element) = newY; + + m_lastX = newX; + m_lastY = newY; + + // Setting node label + ogdf::String& label = m_ogdfAttributes->labelNode(element); switch (node->getNodeType()) { - case st::ControlNode::ntPhi: - label = "Phi"; - m_ogdfAttributes->colorNode(element) = "grey"; - break; - - case st::ControlNode::ntTau: - label = "Tau"; - m_ogdfAttributes->colorNode(element) = "green"; - break; - - case st::ControlNode::ntInstruction: - //label = node.cast()->getInstruction().toString(); - m_ogdfAttributes->colorNode(element) = "blue"; - - default: - label = node->getIndex(); + case st::ControlNode::ntPhi: + label = "Phi"; + m_ogdfAttributes->colorNode(element) = "grey"; + break; + + case st::ControlNode::ntTau: + label = "Tau"; + m_ogdfAttributes->colorNode(element) = "green"; + break; + + case st::ControlNode::ntInstruction: + //label = node.cast()->getInstruction().toString(); + m_ogdfAttributes->colorNode(element) = "blue"; + + default: + label = node->getIndex(); } +} + +ogdf::NodeElement* ControlGraphVisualizer::getNodeFor(st::ControlNode* node) { + TNodeMap::iterator iNode = m_nodes.find(node); + + if (iNode != m_nodes.end()) + return iNode->second; + + ogdf::NodeElement* element = m_ogdfGraph->newNode(node->getIndex()); + setNodeProperties(node, element); m_nodes[node] = element; return element; @@ -99,6 +131,20 @@ void ControlGraphVisualizer::addEdge(st::ControlNode* from, st::ControlNode* to) m_ogdfAttributes->colorEdge(edge) = "grey"; } +void ControlGraphVisualizer::applyLayout() { + ogdf::SugiyamaLayout SL; + SL.setRanking(new ogdf::OptimalRanking); + SL.setCrossMin(new ogdf::MedianHeuristic); + +// ogdf::OptimalHierarchyLayout* ohl = new ogdf::OptimalHierarchyLayout; +// ohl->layerDistance(30.0); +// ohl->nodeDistance(25.0); +// ohl->weightBalancing(0.8); +// SL.setLayout(ohl); + + SL.call(*m_ogdfAttributes); +} + void ControlGraphVisualizer::writeGraphTo(const std::string& fileName) { m_ogdfGraph->writeGML(fileName.c_str()); } From 674d8c32475d700917a2e986c884c8945ee3d3fa Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 10 May 2014 14:48:02 +0700 Subject: [PATCH 042/290] Adds debug logging to ParsedBytecode and fixes parse bugs Issue: #32 --- src/ParsedBytecode.cpp | 59 +++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index df431eb..5be5939 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -1,3 +1,4 @@ +#include #include using namespace st; @@ -10,9 +11,12 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { TByteObject& byteCodes = * m_origin->byteCodes; const uint16_t stopPointer = stopOffset ? stopOffset : byteCodes.getSize(); + std::printf("Phase 1. Collecting branch instructions and building blocks\n"); + // Scaning the method's bytecodes for branch sites and collecting branch targets. // Creating target basic blocks beforehand and collecting them in a map. while (decoder.getBytePointer() < stopPointer) { + const uint16_t currentBytePointer = decoder.getBytePointer(); TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); if (instruction.getOpcode() == opcode::pushBlock) { @@ -25,7 +29,8 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // whether we're in a method or in a block. // Nested blocks are registered in the // container method, not the outer block. - parseBlock(startOffset, stopOffset); + std::printf("%.4u : Parsing smalltalk block in interval [%u:%u)\n", currentBytePointer, startOffset, stopOffset); + //parseBlock(startOffset, stopOffset); // Skipping the nested block's bytecodes decoder.setBytePointer(stopOffset); @@ -37,25 +42,45 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { continue; switch (instruction.getArgument()) { - case special::branch: case special::branchIfTrue: case special::branchIfFalse: { + // Processing skip block + const uint16_t skipOffset = decoder.getBytePointer(); + + // Checking whether current branch target is already known + if (m_offsetToBasicBlock.find(skipOffset) == m_offsetToBasicBlock.end()) { + // Creating the referred basic block and inserting it into the function + // Later it will be filled with instructions and linked to other blocks + BasicBlock* skipBasicBlock = createBasicBlock(skipOffset); + m_offsetToBasicBlock[skipOffset] = skipBasicBlock; + + std::printf("%.4u : branch to skip block %p (%u)\n", currentBytePointer, skipBasicBlock, skipOffset); + } + + } // no break here + + case special::branch: { const uint16_t targetOffset = instruction.getExtra(); // Checking whether current branch target is already known if (m_offsetToBasicBlock.find(targetOffset) == m_offsetToBasicBlock.end()) { // Creating the referred basic block and inserting it into the function // Later it will be filled with instructions and linked to other blocks - BasicBlock* targetBasicBlock = createBasicBlock(decoder.getBytePointer()); + BasicBlock* targetBasicBlock = createBasicBlock(targetOffset); m_offsetToBasicBlock[targetOffset] = targetBasicBlock; + + std::printf("%.4u : branch to target block %p (%u)\n", currentBytePointer, targetBasicBlock, targetOffset); } } break; } } + std::printf("Phase 2. Populating blocks with instructions\n"); + // Populating previously created basic blocks with actual instructions BasicBlock* currentBasicBlock = m_offsetToBasicBlock[startOffset]; + // If no branch site points to start offset then we create block ourselves if (! currentBasicBlock) { m_offsetToBasicBlock[startOffset] = currentBasicBlock = new BasicBlock(startOffset); @@ -64,6 +89,8 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { m_basicBlocks.push_front(currentBasicBlock); } + std::printf("Initial block is %p offset %u\n", currentBasicBlock, currentBasicBlock->getOffset()); + decoder.setBytePointer(startOffset); while (decoder.getBytePointer() < stopPointer) { if (decoder.getBytePointer()) { @@ -80,6 +107,8 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { if (terminator.getExtra() == decoder.getBytePointer()) { // Unconditional branch case assert(terminator.getArgument() == special::branch); + + std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); nextBlock->getReferers().insert(currentBasicBlock); } else { // Previous block referred some other block instead. @@ -89,13 +118,15 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { || terminator.getArgument() == special::branchIfFalse); // Case when branch condition is not met + std::printf("%.4u : block reference %p (%u) ->F %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); nextBlock->getReferers().insert(currentBasicBlock); // Case when branch condition is met const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); - if (iTargetBlock != m_offsetToBasicBlock.end()) + if (iTargetBlock != m_offsetToBasicBlock.end()) { + std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); iTargetBlock->second->getReferers().insert(currentBasicBlock); - else + } else assert(false); } } @@ -103,10 +134,13 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Adding branch instruction to link blocks currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, decoder.getBytePointer())); nextBlock->getReferers().insert(currentBasicBlock); + + std::printf("%.4u : linking blocks %p (%u) -> %p (%u) with branch instruction\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); } // Switching to a new block currentBasicBlock = nextBlock; + std::printf("%.4u : now working on block %p offset %u\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset()); } } @@ -114,12 +148,19 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); currentBasicBlock->append(instruction); - // Final check - if (instruction.isBranch()) { + // Skipping nested smalltalk block bytecodes + if (instruction.getOpcode() == opcode::pushBlock) { + decoder.setBytePointer(instruction.getExtra()); + continue; + } + + // Final check for the last instruction of the method + if (decoder.getBytePointer() >= stopPointer && instruction.isBranch()) { const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(instruction.getExtra()); - if (iTargetBlock != m_offsetToBasicBlock.end()) + if (iTargetBlock != m_offsetToBasicBlock.end()) { iTargetBlock->second->getReferers().insert(currentBasicBlock); - else + std::printf("%.4u : block reference %p (%u) ->? %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + } else assert(false); } } From 5df9bb7f2ed91204dc76f4bfe7bb3e6ba3225ab3 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 10 May 2014 14:49:04 +0700 Subject: [PATCH 043/290] Fixes condition in GraphLinker::getRequestedNode() Issue: #32 --- src/ControlGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index f848dc2..9d2b432 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -294,7 +294,7 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar const TNodeList& refererStack = refererDomain->getLocalStack(); const std::size_t refererStackSize = refererStack.size(); - if (argumentIndex > refererStackSize - 1) { + if (!refererStackSize || argumentIndex > refererStackSize - 1) { // Referer block do not have enough values on it's stack. // We need to go deeper and process it's referers in turn. ControlNode* refererValue = getRequestedNode(refererDomain, argumentIndex); From 10032bc3bd2e459506d418edbf9b301548e18d57 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 10 May 2014 15:09:58 +0700 Subject: [PATCH 044/290] Refactors ParsedBytecode parsing Issue: #32 --- include/instructions.h | 8 +-- src/ParsedBytecode.cpp | 121 +++++++++++++++++++++-------------------- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index ad2e0f7..668a7c0 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -198,10 +198,7 @@ class ParsedBytecode { iterator begin() { return m_basicBlocks.begin(); } iterator end() { return m_basicBlocks.end(); } - BasicBlock* createBasicBlock(uint16_t blockOffset = 0) { - m_basicBlocks.push_back(new BasicBlock(blockOffset)); - return m_basicBlocks.back(); - } + BasicBlock* createBasicBlock(uint16_t blockOffset); virtual ~ParsedBytecode() { for (TBasicBlockList::iterator iBlock = m_basicBlocks.begin(), @@ -235,6 +232,9 @@ class ParsedBytecode { typedef std::map TOffsetToBasicBlockMap; TOffsetToBasicBlockMap m_offsetToBasicBlock; + +private: + void updateReferences(BasicBlock* currentBasicBlock, BasicBlock* nextBlock, InstructionDecoder& decoder); }; class ParsedBlock; diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 5be5939..c8b0b38 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -45,32 +45,16 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { case special::branchIfTrue: case special::branchIfFalse: { // Processing skip block - const uint16_t skipOffset = decoder.getBytePointer(); - - // Checking whether current branch target is already known - if (m_offsetToBasicBlock.find(skipOffset) == m_offsetToBasicBlock.end()) { - // Creating the referred basic block and inserting it into the function - // Later it will be filled with instructions and linked to other blocks - BasicBlock* skipBasicBlock = createBasicBlock(skipOffset); - m_offsetToBasicBlock[skipOffset] = skipBasicBlock; - - std::printf("%.4u : branch to skip block %p (%u)\n", currentBytePointer, skipBasicBlock, skipOffset); - } - + const uint16_t skipOffset = decoder.getBytePointer(); + BasicBlock* skipBasicBlock = createBasicBlock(skipOffset); + std::printf("%.4u : branch to skip block %p (%u)\n", currentBytePointer, skipBasicBlock, skipOffset); } // no break here case special::branch: { - const uint16_t targetOffset = instruction.getExtra(); - - // Checking whether current branch target is already known - if (m_offsetToBasicBlock.find(targetOffset) == m_offsetToBasicBlock.end()) { - // Creating the referred basic block and inserting it into the function - // Later it will be filled with instructions and linked to other blocks - BasicBlock* targetBasicBlock = createBasicBlock(targetOffset); - m_offsetToBasicBlock[targetOffset] = targetBasicBlock; - - std::printf("%.4u : branch to target block %p (%u)\n", currentBytePointer, targetBasicBlock, targetOffset); - } + // Processing target block + const uint16_t targetOffset = instruction.getExtra(); + BasicBlock* targetBasicBlock = createBasicBlock(targetOffset); + std::printf("%.4u : branch to target block %p (%u)\n", currentBytePointer, targetBasicBlock, targetOffset); } break; } } @@ -101,42 +85,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Checking if previous block referred current block // by jumping into it and adding reference if needed - TSmalltalkInstruction terminator(opcode::extended); - if (currentBasicBlock->getTerminator(terminator)) { - if (terminator.isBranch()) { - if (terminator.getExtra() == decoder.getBytePointer()) { - // Unconditional branch case - assert(terminator.getArgument() == special::branch); - - std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); - nextBlock->getReferers().insert(currentBasicBlock); - } else { - // Previous block referred some other block instead. - // Terminator is one of conditional branch instructions. - // We need to refer both of branch targets here. - assert(terminator.getArgument() == special::branchIfTrue - || terminator.getArgument() == special::branchIfFalse); - - // Case when branch condition is not met - std::printf("%.4u : block reference %p (%u) ->F %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); - nextBlock->getReferers().insert(currentBasicBlock); - - // Case when branch condition is met - const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); - if (iTargetBlock != m_offsetToBasicBlock.end()) { - std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); - iTargetBlock->second->getReferers().insert(currentBasicBlock); - } else - assert(false); - } - } - } else { - // Adding branch instruction to link blocks - currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, decoder.getBytePointer())); - nextBlock->getReferers().insert(currentBasicBlock); - - std::printf("%.4u : linking blocks %p (%u) -> %p (%u) with branch instruction\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); - } + updateReferences(currentBasicBlock, nextBlock, decoder); // Switching to a new block currentBasicBlock = nextBlock; @@ -165,3 +114,57 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { } } } + +BasicBlock* ParsedBytecode::createBasicBlock(uint16_t blockOffset) { + // Checking whether current branch target is already known + TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(blockOffset); + if (iBlock != m_offsetToBasicBlock.end()) + return iBlock->second; + + // Creating the referred basic block and inserting it into the function + // Later it will be filled with instructions and linked to other blocks + BasicBlock* newBasicBlock = new BasicBlock(blockOffset); + m_offsetToBasicBlock[blockOffset] = newBasicBlock; + m_basicBlocks.push_back(newBasicBlock); + + return newBasicBlock; +} + +void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* nextBlock, InstructionDecoder& decoder) { + TSmalltalkInstruction terminator(opcode::extended); + if (currentBasicBlock->getTerminator(terminator)) { + if (terminator.isBranch()) { + if (terminator.getExtra() == decoder.getBytePointer()) { + // Unconditional branch case + assert(terminator.getArgument() == special::branch); + + std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); + nextBlock->getReferers().insert(currentBasicBlock); + } else { + // Previous block referred some other block instead. + // Terminator is one of conditional branch instructions. + // We need to refer both of branch targets here. + assert(terminator.getArgument() == special::branchIfTrue + || terminator.getArgument() == special::branchIfFalse); + + // Case when branch condition is not met + std::printf("%.4u : block reference %p (%u) ->F %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); + nextBlock->getReferers().insert(currentBasicBlock); + + // Case when branch condition is met + const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); + if (iTargetBlock != m_offsetToBasicBlock.end()) { + std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + iTargetBlock->second->getReferers().insert(currentBasicBlock); + } else + assert(false); + } + } + } else { + // Adding branch instruction to link blocks + currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, decoder.getBytePointer())); + nextBlock->getReferers().insert(currentBasicBlock); + + std::printf("%.4u : linking blocks %p (%u) -> %p (%u) with branch instruction\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); + } +} \ No newline at end of file From 973c2d4111f432ab2670e075b9c2d4ee680b33ab Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 00:11:15 +0700 Subject: [PATCH 045/290] Adds debug logging to control graph builder, fixes bugs Issue: #32 --- src/ControlGraph.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 9d2b432..bba4b72 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -1,3 +1,4 @@ +#include #include using namespace st; @@ -24,6 +25,8 @@ class GraphConstructor : public InstructionVisitor { m_currentDomain = m_graph->getDomainFor(&basicBlock); m_currentDomain->setBasicBlock(&basicBlock); m_skipStubInstructions = false; + + std::printf("GraphConstructor::visitBlock : block %p (%.2u), domain %p\n", &basicBlock, basicBlock.getOffset(), m_currentDomain); return InstructionVisitor::visitBlock(basicBlock); } @@ -38,6 +41,11 @@ class GraphConstructor : public InstructionVisitor { m_currentDomain->addNode(newNode); // Processing instruction by adding references + std::printf("GraphConstructor::visitInstruction : processing node %.2u %s%s \n", + newNode->getIndex(), + newNode->getInstruction().isBranch() ? "^" : "", + newNode->getInstruction().isTerminator() ? "!" : "" + ); processNode(newNode); return true; @@ -72,19 +80,27 @@ void GraphConstructor::processNode(InstructionNode* node) case opcode::assignTemporary: // TODO Link with tau node case opcode::assignInstance: + // FIXME should not pop/push the stack + m_currentDomain->requestArgument(0, node); + m_currentDomain->pushValue(node); + break; + case opcode::sendUnary: case opcode::sendMessage: m_currentDomain->requestArgument(0, node); + m_currentDomain->pushValue(node); break; case opcode::sendBinary: m_currentDomain->requestArgument(1, node); m_currentDomain->requestArgument(0, node); + m_currentDomain->pushValue(node); break; case opcode::markArguments: for (uint32_t index = instruction.getArgument(); index > 0; ) m_currentDomain->requestArgument(--index, node); + m_currentDomain->pushValue(node); break; case opcode::doSpecial: @@ -93,6 +109,7 @@ void GraphConstructor::processNode(InstructionNode* node) case opcode::doPrimitive: processPrimitives(node); + m_currentDomain->pushValue(node); break; default: @@ -109,6 +126,14 @@ void GraphConstructor::processSpecials(InstructionNode* node) case special::blockReturn: case special::sendToSuper: m_currentDomain->requestArgument(0, node); + + assert(! m_currentDomain->getTerminator()); + m_currentDomain->setTerminator(node); + + // All instructions that go after terminator within current block + // are stubs that were added by the image builder. Control flow + // will never reach such instructions, so they may be ignored safely. + m_skipStubInstructions = true; break; case special::duplicate: @@ -203,6 +228,18 @@ class GraphLinker : public NodeVisitor { virtual bool visitDomain(ControlDomain& domain) { m_currentDomain = &domain; + std::printf("GraphLinker::visitDomain : processing domain %p, block offset %.2u, referrers %u, local stack %u, requested args %.2u\n", + &domain, + domain.getBasicBlock()->getOffset(), + domain.getBasicBlock()->getReferers().size(), + domain.getLocalStack().size(), + domain.getRequestedArguments().size() + ); + + for (std::size_t index = 0; index < domain.getRequestedArguments().size(); index++) + std::printf("GraphLinker::visitDomain : arg request %u, node index %.2u\n", + index, domain.getRequestedArguments()[index].requestingNode->getIndex()); + processBranching(); processArgumentRequests(); @@ -322,6 +359,7 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar void ControlGraph::buildGraph() { // Iterating through basic blocks of parsed method and constructing node domains + std::printf("Phase 1. Constructing control graph\n"); GraphConstructor constructor(this); constructor.run(); @@ -329,6 +367,7 @@ void ControlGraph::buildGraph() // They're linked using phi nodes or a direct link if possible. // Also branching edges are added so graph remains linked even if // no stack relations exist. + std::printf("Phase 2. Linking control graph\n"); GraphLinker linker(this); linker.run(); } From ca65ea13968a70db6436e10741316afe4bbfe29b Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 12:34:23 +0700 Subject: [PATCH 046/290] Fixes parser by cleaning reference graph by eliminating dead code Issue: #32 --- src/ParsedBytecode.cpp | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index c8b0b38..1fb22c1 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -75,11 +75,18 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { std::printf("Initial block is %p offset %u\n", currentBasicBlock, currentBasicBlock->getOffset()); + // Instructions in a basic block that follow a terminator instruction + // will never be executed because control flow will never reach them. + // We need to skip such instructions because they may distort the actual + // control flow by introducing fake block dependencies. + bool terminatorEncoded = false; + decoder.setBytePointer(startOffset); while (decoder.getBytePointer() < stopPointer) { - if (decoder.getBytePointer()) { + const uint16_t currentBytePointer = decoder.getBytePointer(); + if (currentBytePointer) { // Switching basic block if current offset is a branch target - const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(decoder.getBytePointer()); + const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(currentBytePointer); if (iBlock != m_offsetToBasicBlock.end()) { BasicBlock* const nextBlock = iBlock->second; @@ -89,13 +96,16 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Switching to a new block currentBasicBlock = nextBlock; - std::printf("%.4u : now working on block %p offset %u\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset()); + + // Resetting the terminator flag + terminatorEncoded = false; + + std::printf("%.4u : now working on block %p offset %u\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset()); } } // Fetching instruction and appending it to the current basic block TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); - currentBasicBlock->append(instruction); // Skipping nested smalltalk block bytecodes if (instruction.getOpcode() == opcode::pushBlock) { @@ -103,12 +113,25 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { continue; } + // Skipping dead code + if (terminatorEncoded) { + std::printf("%.4u : skipping dead code\n", currentBytePointer); + continue; + } else { + currentBasicBlock->append(instruction); + } + + if (instruction.isTerminator()) { + std::printf("%.4u : terminator encoded\n", currentBytePointer); + terminatorEncoded = true; + } + // Final check for the last instruction of the method if (decoder.getBytePointer() >= stopPointer && instruction.isBranch()) { const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(instruction.getExtra()); if (iTargetBlock != m_offsetToBasicBlock.end()) { iTargetBlock->second->getReferers().insert(currentBasicBlock); - std::printf("%.4u : block reference %p (%u) ->? %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + std::printf("%.4u : block reference %p (%u) ->? %p (%u)\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); } else assert(false); } From 38b1aed6f8d5c1ef9e6d37a58b6d66bd40a23c95 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 12:42:39 +0700 Subject: [PATCH 047/290] Removes unneeded m_skipStubInstructions logic from GraphConstructor Issue: #32 --- src/ControlGraph.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index bba4b72..2973519 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -19,21 +19,17 @@ template<> TauNode* ControlGraph::newNode() { return static_castgetParsedMethod()), m_graph(graph), m_skipStubInstructions(false) { } + : InstructionVisitor(graph->getParsedMethod()), m_graph(graph) { } virtual bool visitBlock(BasicBlock& basicBlock) { m_currentDomain = m_graph->getDomainFor(&basicBlock); m_currentDomain->setBasicBlock(&basicBlock); - m_skipStubInstructions = false; std::printf("GraphConstructor::visitBlock : block %p (%.2u), domain %p\n", &basicBlock, basicBlock.getOffset(), m_currentDomain); return InstructionVisitor::visitBlock(basicBlock); } virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { - if (m_skipStubInstructions) - return true; - // Initializing instruction node InstructionNode* newNode = m_graph->newNode(); newNode->setInstruction(instruction); @@ -129,11 +125,6 @@ void GraphConstructor::processSpecials(InstructionNode* node) assert(! m_currentDomain->getTerminator()); m_currentDomain->setTerminator(node); - - // All instructions that go after terminator within current block - // are stubs that were added by the image builder. Control flow - // will never reach such instructions, so they may be ignored safely. - m_skipStubInstructions = true; break; case special::duplicate: @@ -152,11 +143,6 @@ void GraphConstructor::processSpecials(InstructionNode* node) case special::branch: assert(! m_currentDomain->getTerminator()); m_currentDomain->setTerminator(node); - - // All instructions that go after terminator within current block - // are stubs that were added by the image builder. Control flow - // will never reach such instructions, so they may be ignored safely. - m_skipStubInstructions = true; break; } } From 2c7bb36ba86c84da6cbf61e48168cc126c1543c9 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 19:14:33 +0700 Subject: [PATCH 048/290] Adds comparator for control graph domains This helps graph visitors to return contents in the natural order by increasing domain offsets. Issue: #32 --- include/analysis.h | 16 ++++++++++------ src/ControlGraph.cpp | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index a4c9653..8c592e3 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -64,14 +64,19 @@ class ControlDomain; class ControlNode; typedef std::vector TNodeList; -typedef std::set TDomainSet; class NodeIndexCompare { public: bool operator() (const ControlNode* a, const ControlNode* b); }; +class DomainOffsetCompare { +public: + bool operator() (const ControlDomain* a, const ControlDomain* b); +}; + typedef std::set TNodeSet; +typedef std::set TDomainSet; // ControlNode is a base class for elements of ControlGraph. // Elements of a graph represent various types of relations @@ -236,7 +241,7 @@ class ControlDomain { const TRequestList& getRequestedArguments() const { return m_reqestedArguments; } const TNodeList& getLocalStack() const { return m_localStack; } - ControlDomain() : m_entryPoint(0), m_terminator(0), m_basicBlock(0) { } + ControlDomain(BasicBlock* basicBlock) : m_entryPoint(0), m_terminator(0), m_basicBlock(basicBlock) { } private: TNodeSet m_nodes; InstructionNode* m_entryPoint; @@ -281,8 +286,8 @@ class ControlGraph { template T* newNode(); - ControlDomain* newDomain() { - ControlDomain* domain = new ControlDomain; + ControlDomain* newDomain(BasicBlock* basicBlock) { + ControlDomain* domain = new ControlDomain(basicBlock); m_domains.insert(domain); return domain; } @@ -302,8 +307,7 @@ class ControlGraph { ControlDomain* getDomainFor(BasicBlock* basicBlock) { TDomainMap::iterator iDomain = m_blocksToDomains.find(basicBlock); if (iDomain == m_blocksToDomains.end()) { - ControlDomain* const domain = newDomain(); - domain->setBasicBlock(basicBlock); + ControlDomain* const domain = newDomain(basicBlock); m_blocksToDomains[basicBlock] = domain; return domain; } diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 2973519..9fbce0b 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -8,6 +8,10 @@ bool NodeIndexCompare::operator() (const ControlNode* a, const ControlNode* b) return a->getIndex() < b->getIndex(); } +bool DomainOffsetCompare::operator() (const ControlDomain* a, const ControlDomain* b) { + return a->getBasicBlock()->getOffset() < b->getBasicBlock()->getOffset(); +} + template<> InstructionNode* ControlNode::cast() { return this->getNodeType() == ntInstruction ? static_cast(this) : 0; } template<> PhiNode* ControlNode::cast() { return this->getNodeType() == ntPhi ? static_cast(this) : 0; } template<> TauNode* ControlNode::cast() { return this->getNodeType() == ntTau ? static_cast(this) : 0; } From 750cc1fe1952e8b10eabec61b97c41559740afa1 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 19:57:22 +0700 Subject: [PATCH 049/290] Rewrites graph visualizator using graphviz *nix way rocks! Issue: #32 --- CMakeLists.txt | 4 +- include/visualization.h | 37 ++++---- src/ControlGraphVisualizer.cpp | 164 ++++++++++++--------------------- 3 files changed, 80 insertions(+), 125 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0707e7..191b876 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,6 @@ set (CMAKE_CXX_FLAGS_RELEASE "-O3") add_subdirectory(src) add_subdirectory(include) include_directories(include) -include_directories(ogdf) # Base set of sources needed in every build set(CPP_FILES @@ -89,8 +88,7 @@ if (LLVM) endif() add_executable(llst ${CPP_FILES}) -set_target_properties(llst PROPERTIES LINK_FLAGS "-L../ogdf/_release") -target_link_libraries(llst ${LLVM_LIBS} ${READLINE_LIB} ${LLVM_LD_FLAGS} OGDF) +target_link_libraries(llst ${LLVM_LIBS} ${READLINE_LIB} ${LLVM_LD_FLAGS}) if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) # this is a 64-bit OS diff --git a/include/visualization.h b/include/visualization.h index cfc883b..5cc2016 100644 --- a/include/visualization.h +++ b/include/visualization.h @@ -1,37 +1,38 @@ #ifndef LLST_VISUALIZATION_H_INCLUDED #define LLST_VISUALIZATION_H_INCLUDED +#include +#include +#include + #include #include -#include -#include - class ControlGraphVisualizer : public st::NodeVisitor { public: - ControlGraphVisualizer(st::ControlGraph* graph) : st::NodeVisitor(graph) { m_ogdfGraph = new ogdf::Graph(); } - virtual ~ControlGraphVisualizer() { delete m_ogdfGraph; } + ControlGraphVisualizer(st::ControlGraph* graph, const std::string& fileName) : st::NodeVisitor(graph) { + m_stream.open(fileName.c_str(), std::ios::out | std::ios::trunc); + + m_stream << "digraph G2 {\n"; + firstDomain = true; + } + + virtual ~ControlGraphVisualizer() { finish(); } virtual bool visitDomain(st::ControlDomain& domain); virtual bool visitNode(st::ControlNode& node); - void writeGraphTo(const std::string& fileName); - private: - ogdf::NodeElement* getNodeFor(st::ControlNode*); - void addEdge(st::ControlNode* from, st::ControlNode* to); - void applyLayout(); - void setNodeProperties(st::ControlNode* node, ogdf::NodeElement* element); + void finish(); + bool isNodeProcessed(st::ControlNode* node); + void markNode(st::ControlNode* node); private: - ogdf::Graph* m_ogdfGraph; - ogdf::GraphAttributes* m_ogdfAttributes; - - typedef std::map TNodeMap; - TNodeMap m_nodes; + typedef std::map TNodeMap; + TNodeMap m_processedNodes; - uint32_t m_lastX; - uint32_t m_lastY; + std::ofstream m_stream; + bool firstDomain; }; #endif diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index a0f472b..cfed40a 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -1,150 +1,106 @@ #include -#include -#include -#include -#include - bool ControlGraphVisualizer::visitDomain(st::ControlDomain& domain) { - // TODO Create domain frame +// if (!firstDomain) +// m_stream << "\t} \n" << std::endl; // closing subgraph + + firstDomain = false; +// m_stream << "\n\tsubgraph cluster_" << domain.getBasicBlock()->getOffset() << " {\n"; return st::NodeVisitor::visitDomain(domain); } -bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { - ogdf::NodeElement* element = getNodeFor(&node); +std::string edgeStyle(st::ControlNode* from, st::ControlNode* to) { + const st::InstructionNode* fromInstruction = from->cast(); + const st::InstructionNode* toInstruction = to->cast(); + + if ((fromInstruction && fromInstruction->getInstruction().isBranch()) || + (toInstruction && toInstruction->getArgumentsCount() == 0)) + { + return "[color=\"grey\" style=\"dashed\"]"; + } + + return ""; +} +bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { const st::TNodeSet& inEdges = node.getInEdges(); const st::TNodeSet& outEdges = node.getOutEdges(); // Processing incoming edges st::TNodeSet::iterator iEdge = inEdges.begin(); - for (; iEdge != inEdges.end(); ++iEdge) - addEdge(*iEdge, &node); + for (; iEdge != inEdges.end(); ++iEdge) { + if (isNodeProcessed(*iEdge)) + continue; + + m_stream << "\t\t" << (*iEdge)->getIndex() << " -> " << node.getIndex() << edgeStyle(*iEdge, &node) << ";\n"; + } // Processing outgoing edges iEdge = outEdges.begin(); - for (; iEdge != outEdges.end(); ++iEdge) - addEdge(&node, *iEdge); + for (; iEdge != outEdges.end(); ++iEdge) { + if (isNodeProcessed(*iEdge)) + continue; + + m_stream << "\t\t" << node.getIndex() << " -> " << (*iEdge)->getIndex() << edgeStyle(&node, *iEdge) << ";\n"; + } // Processing argument edges if (const st::InstructionNode* instruction = node.cast()) { - for (std::size_t index = 0; index < instruction->getArgumentsCount(); index++) { - ogdf::NodeElement* argument = getNodeFor(instruction->getArgument(index)); - ogdf::EdgeElement* edge = m_ogdfGraph->newEdge(element, argument); + const std::size_t argsCount = instruction->getArgumentsCount(); + for (std::size_t index = 0; index < argsCount; index++) { + m_stream << "\t\t" << node.getIndex() << " -> " << instruction->getArgument(index)->getIndex() << " ["; - m_ogdfAttributes->labelEdge(edge) = index; - m_ogdfAttributes->colorEdge(edge) = "blue"; - m_ogdfAttributes->arrowEdge(edge) = ogdf::GraphAttributes::first; + if (argsCount > 1) + m_stream << "label=" << index; + + m_stream << " labelfloat=true color=\"blue\" fontcolor=\"blue\" style=\"dashed\" constraint=false];\n"; } } + markNode(&node); return st::NodeVisitor::visitNode(node); } -void ControlGraphVisualizer::setNodeProperties(st::ControlNode* node, ogdf::NodeElement* element) { - // Setting basic attributes - m_ogdfAttributes->shapeNode(element) = ogdf::GraphAttributes::oval; - m_ogdfAttributes->width(element) = 32; - m_ogdfAttributes->height(element) = 32; - - // Calculating node position - uint32_t newX = 0; - uint32_t newY = 0; - - // We do not want to apply complex layout because our graph is pretty straighforward. - // So placing nodes in natural order but stacking argument nodes where possible. - if (node->getInEdges().size()) { - // Shifting to the next colon - newX = m_lastX + 64; - newY = 0; - } else { - // Stacking it with previous one. - newX = m_lastX; - newY = m_lastY + 64; - } - - m_ogdfAttributes->x(element) = newX; - m_ogdfAttributes->y(element) = newY; - - m_lastX = newX; - m_lastY = newY; +bool ControlGraphVisualizer::isNodeProcessed(st::ControlNode* node) { + return m_processedNodes.find(node) != m_processedNodes.end(); +} +void ControlGraphVisualizer::markNode(st::ControlNode* node) { // Setting node label - ogdf::String& label = m_ogdfAttributes->labelNode(element); + std::string label; + std::string color; + switch (node->getNodeType()) { case st::ControlNode::ntPhi: - label = "Phi"; - m_ogdfAttributes->colorNode(element) = "grey"; + label = "Phi "; + color = "grey"; break; case st::ControlNode::ntTau: - label = "Tau"; - m_ogdfAttributes->colorNode(element) = "green"; + label = "Tau "; + color = "green"; break; case st::ControlNode::ntInstruction: //label = node.cast()->getInstruction().toString(); - m_ogdfAttributes->colorNode(element) = "blue"; + if (node->cast()->getInstruction().isTerminator()) + color = "red"; + break; default: - label = node->getIndex(); - } -} - -ogdf::NodeElement* ControlGraphVisualizer::getNodeFor(st::ControlNode* node) { - TNodeMap::iterator iNode = m_nodes.find(node); - - if (iNode != m_nodes.end()) - return iNode->second; - - ogdf::NodeElement* element = m_ogdfGraph->newNode(node->getIndex()); - setNodeProperties(node, element); - - m_nodes[node] = element; - return element; -} - -void ControlGraphVisualizer::addEdge(st::ControlNode* from, st::ControlNode* to) { - ogdf::NodeElement* fromElement = getNodeFor(from); - ogdf::NodeElement* toElement = getNodeFor(to); - - ogdf::EdgeElement* edge = m_ogdfGraph->searchEdge(fromElement, toElement); - if (edge) // Skipping if edge already exists - return; - - edge = m_ogdfGraph->newEdge(fromElement, toElement); - m_ogdfAttributes->arrowEdge(edge) = ogdf::GraphAttributes::first; - - const st::InstructionNode* fromInstruction = from->cast(); - const st::InstructionNode* toInstruction = to->cast(); - - if ((fromInstruction && fromInstruction->getInstruction().isBranch()) || - (toInstruction && toInstruction->getArgumentsCount() == 0)) - { - m_ogdfAttributes->colorEdge(edge) = "grey"; - m_ogdfAttributes->styleEdge(edge) = ogdf::GraphAttributes::esDash; + ; } - if (to->getNodeType() == st::ControlNode::ntPhi) - m_ogdfAttributes->colorEdge(edge) = "grey"; + m_stream << "\t\t" << label << node->getIndex() << " [ color=\"" << color << "\"];\n"; + m_processedNodes[node] = true; } -void ControlGraphVisualizer::applyLayout() { - ogdf::SugiyamaLayout SL; - SL.setRanking(new ogdf::OptimalRanking); - SL.setCrossMin(new ogdf::MedianHeuristic); - -// ogdf::OptimalHierarchyLayout* ohl = new ogdf::OptimalHierarchyLayout; -// ohl->layerDistance(30.0); -// ohl->nodeDistance(25.0); -// ohl->weightBalancing(0.8); -// SL.setLayout(ohl); - - SL.call(*m_ogdfAttributes); -} +void ControlGraphVisualizer::finish() { +// if (!firstDomain) +// m_stream << "\t} \n"; -void ControlGraphVisualizer::writeGraphTo(const std::string& fileName) { - m_ogdfGraph->writeGML(fileName.c_str()); + m_stream << "} \n"; + m_stream.close(); } From d3a67520b0a38e3b2bb3ac7aaaf6d38216b9bd87 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 23 Apr 2014 00:31:30 +0400 Subject: [PATCH 050/290] Moves some code of TSmalltalkInstruction class from headers into .cpp file Removes TInstruction from types.h. Adds method TSmalltalkInstruction::toString(). Issue: #32 --- CMakeLists.txt | 2 +- include/instructions.h | 37 +----- include/types.h | 9 -- ...truction.cpp => TSmalltalkInstruction.cpp} | 120 +++++++++--------- 4 files changed, 64 insertions(+), 104 deletions(-) rename src/{TInstruction.cpp => TSmalltalkInstruction.cpp} (56%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 191b876..e33bd3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,10 +63,10 @@ set(CPP_FILES src/NonCollectMemoryManager.cpp src/primitives.cpp src/TDictionary.cpp - src/TInstruction.cpp src/TSymbol.cpp src/vm.cpp + src/TSmalltalkInstruction.cpp src/InstructionDecoder.cpp src/ParsedMethod.cpp src/ParsedBytecode.cpp diff --git a/include/instructions.h b/include/instructions.h index 668a7c0..6f67a0b 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -40,40 +40,9 @@ struct TSmalltalkInstruction { static_cast(m_argument) << 8 | static_cast(m_extra) << 16; } - - bool isTerminator() const { - if (m_opcode != opcode::doSpecial) - return false; - - if (isBranch()) - return true; - - switch (m_argument) { - case special::stackReturn: - case special::selfReturn: - case special::blockReturn: - return true; - - default: - return false; - } - } - - bool isBranch() const { - if (m_opcode != opcode::doSpecial) - return false; - - switch (m_argument) { - case special::branch: - case special::branchIfFalse: - case special::branchIfTrue: - return true; - - default: - return false; - } - } - + bool isTerminator() const; + bool isBranch() const; + std::string toString() const; private: TOpcode m_opcode; TArgument m_argument; diff --git a/include/types.h b/include/types.h index 27b40db..55564b5 100644 --- a/include/types.h +++ b/include/types.h @@ -376,13 +376,4 @@ struct TProcess : public TObject { static const char* InstanceClassName() { return "Process"; } }; -// TInstruction represents one decoded Smalltalk instruction. -// Actual meaning of parts is determined during the execution. -struct TInstruction { - uint8_t low; - uint8_t high; - - std::string toString(); -}; - #endif diff --git a/src/TInstruction.cpp b/src/TSmalltalkInstruction.cpp similarity index 56% rename from src/TInstruction.cpp rename to src/TSmalltalkInstruction.cpp index 5ec2d54..21a7367 100644 --- a/src/TInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -1,60 +1,70 @@ -/* - * TInstruction.cpp - * - * Helper functions for TInstruction class - * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.2 - * - * LLST is - * Copyright (C) 2012-2013 by Dmitry Kashitsyn - * Copyright (C) 2012-2013 by Roman Proskuryakov - * - * LLST is based on the LittleSmalltalk which is - * Copyright (C) 1987-2005 by Timothy A. Budd - * Copyright (C) 2007 by Charles R. Childers - * Copyright (C) 2005-2007 by Danny Reinhold - * - * Original license of LittleSmalltalk may be found in the LICENSE file. - * - * - * This file is part of LLST. - * LLST is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LLST 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with LLST. If not, see . - */ - -#include -#include +#include #include -std::string TInstruction::toString() +bool st::TSmalltalkInstruction::isTerminator() const +{ + if (m_opcode != opcode::doSpecial) + return false; + + if (isBranch()) + return true; + + switch (m_argument) { + case special::stackReturn: + case special::selfReturn: + case special::blockReturn: + return true; + + default: + return false; + } +} + +bool st::TSmalltalkInstruction::isBranch() const +{ + if (m_opcode != opcode::doSpecial) + return false; + + switch (m_argument) { + case special::branch: + case special::branchIfFalse: + case special::branchIfTrue: + return true; + + default: + return false; + } +} + +std::string st::TSmalltalkInstruction::toString() const { std::ostringstream ss; - int iHigh = high; - int iLow = low; + int argument = m_argument; // They should be ints to be displayed + int extra = m_extra; // correctly by stringstream std::ostringstream errSs; - errSs << "Unknown instrunction {" << iHigh << ", " << iLow << "}"; + errSs << "Unknown instrunction {" << m_opcode << ", " << argument << ", " << extra << "}"; - switch(high) + switch(m_opcode) { - case opcode::pushInstance: ss << "PushInstance " << iLow; break; - case opcode::pushArgument: ss << "PushArgument " << iLow; break; - case opcode::pushTemporary: ss << "PushTemporary " << iLow; break; - case opcode::pushLiteral: ss << "PushLiteral " << iLow; break; + case opcode::pushInstance: ss << "PushInstance " << argument; break; + case opcode::pushArgument: ss << "PushArgument " << argument; break; + case opcode::pushTemporary: ss << "PushTemporary " << argument; break; + case opcode::pushLiteral: ss << "PushLiteral " << argument; break; + case opcode::pushBlock: ss << "PushBlock " << argument; break; + case opcode::assignTemporary: ss << "AssignTemporary " << argument; break; + case opcode::assignInstance: ss << "AssignInstance " << argument; break; + case opcode::markArguments: ss << "MarkArguments " << argument; break; + case opcode::sendMessage: ss << "SendMessage "; break; + + case opcode::doPrimitive: { + ss << "Primitive " << extra << " (" << argument << " arguments)"; + } break; + case opcode::pushConstant: { ss << "PushConstant "; - switch(low) { + switch(argument) { case 0: case 1: case 2: @@ -65,7 +75,7 @@ std::string TInstruction::toString() case 7: case 8: case 9: - ss << iLow; + ss << argument; break; case pushConstants::nil: ss << "nil"; break; case pushConstants::trueObject: ss << "true"; break; @@ -75,16 +85,9 @@ std::string TInstruction::toString() } } } break; - case opcode::pushBlock: ss << "PushBlock " << iLow; break; - - case opcode::assignTemporary: ss << "AssignTemporary " << iLow; break; - case opcode::assignInstance: ss << "AssignInstance " << iLow; break; - - case opcode::markArguments: ss << "MarkArguments " << iLow; break; - case opcode::sendUnary: { ss << "SendUnary "; - switch(low) { + switch(argument) { case unaryBuiltIns::isNil: ss << "isNil"; break; case unaryBuiltIns::notNil: ss << "isNotNil"; break; default: { @@ -94,7 +97,7 @@ std::string TInstruction::toString() } break; case opcode::sendBinary: { ss << "SendBinary "; - switch(low) { + switch(argument) { case binaryBuiltIns::operatorPlus: ss << "+"; break; case binaryBuiltIns::operatorLess: ss << "<"; break; case binaryBuiltIns::operatorLessOrEq: ss << "<="; break; @@ -103,11 +106,9 @@ std::string TInstruction::toString() } } } break; - case opcode::sendMessage: ss << "SendMessage "; break; - case opcode::doSpecial: { ss << "Special "; - switch(low) { + switch(argument) { case special::selfReturn: ss << "selfReturn"; break; case special::stackReturn: ss << "stackReturn"; break; case special::blockReturn: ss << "blockReturn"; break; @@ -122,7 +123,6 @@ std::string TInstruction::toString() } } } break; - case opcode::doPrimitive: ss << "Primitive"; break; default: { throw std::runtime_error(errSs.str()); From 4cec0479f21b38cfe572e7a14bc611d03c8b0451 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 20:30:58 +0700 Subject: [PATCH 051/290] Fixes node linking in control graph Issue: #32 --- src/ControlGraph.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 9fbce0b..8d43de7 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -256,6 +256,10 @@ class GraphLinker : public NodeVisitor { m_nodeToLink = 0; } + InstructionNode* instruction = node.cast(); + if (instruction && instruction->getInstruction().isTerminator()) + return; // terminator nodes will take care of themselves + TNodeSet& outEdges = node.getOutEdges(); TNodeSet::iterator iNode = outEdges.begin(); bool isNodeLinked = false; From 797c870911c87dc254389163e3d8acd4cfe43fb7 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 23:54:14 +0700 Subject: [PATCH 052/290] Adds more logging to control graph Issue: #32 --- src/ControlGraph.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 8d43de7..791b30e 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -252,6 +252,7 @@ class GraphLinker : public NodeVisitor { // Linking pending node if (m_nodeToLink) { + std::printf("GraphLinker::processNode : fallback linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); m_nodeToLink->addEdge(&node); m_nodeToLink = 0; } @@ -288,6 +289,7 @@ class GraphLinker : public NodeVisitor { InstructionNode* const terminator = refererDomain->getTerminator(); assert(terminator && terminator->getInstruction().isBranch()); + std::printf("GraphLinker::processNode : linking nodes of referring graphs %.2u and %.2u\n", terminator->getIndex(), entryPoint->getIndex()); terminator->addEdge(entryPoint); } } @@ -300,6 +302,8 @@ class GraphLinker : public NodeVisitor { void processRequest(ControlDomain* domain, std::size_t argumentIndex, const ControlDomain::TArgumentRequest& request) { ControlNode* node = getRequestedNode(domain, argumentIndex); + + std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", node->getIndex(), request.requestingNode->getIndex()); node->addEdge(request.requestingNode); request.requestingNode->setArgument(request.index, node); } From 2478bef51d40d0a2c7ee9096bb0bbd52a475adf8 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 23:55:11 +0700 Subject: [PATCH 053/290] Fixes referrers processing in ParsedBytecode Issue: #32 --- src/ParsedBytecode.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 1fb22c1..625bb90 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -157,12 +157,15 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* TSmalltalkInstruction terminator(opcode::extended); if (currentBasicBlock->getTerminator(terminator)) { if (terminator.isBranch()) { - if (terminator.getExtra() == decoder.getBytePointer()) { + if (terminator.getArgument() == special::branch) { // Unconditional branch case - assert(terminator.getArgument() == special::branch); - - std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); - nextBlock->getReferers().insert(currentBasicBlock); + const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); + if (iTargetBlock != m_offsetToBasicBlock.end()) { + std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + iTargetBlock->second->getReferers().insert(currentBasicBlock); + } else { + assert(false); + } } else { // Previous block referred some other block instead. // Terminator is one of conditional branch instructions. @@ -179,8 +182,9 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* if (iTargetBlock != m_offsetToBasicBlock.end()) { std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); iTargetBlock->second->getReferers().insert(currentBasicBlock); - } else + } else { assert(false); + } } } } else { From 8b91192eed64201fc90b8791b15f073db846d1b9 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 23:56:33 +0700 Subject: [PATCH 054/290] Writes instruction mnemonic as a graph node's label Issue: #32 --- src/ControlGraphVisualizer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index cfed40a..459a8c6 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -84,7 +84,7 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { break; case st::ControlNode::ntInstruction: - //label = node.cast()->getInstruction().toString(); + label = node->cast()->getInstruction().toString(); if (node->cast()->getInstruction().isTerminator()) color = "red"; break; @@ -93,7 +93,8 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { ; } - m_stream << "\t\t" << label << node->getIndex() << " [ color=\"" << color << "\"];\n"; + m_stream << "\t\t " << node->getIndex() << "[shape=box label=\"" << node->getIndex() << " : " << label << "\" color=\"" << color << "\"];\n"; +// m_stream << "\t\t " << node->getIndex() << "[label=\"" << node->getIndex() /*<< " : " << label */<< "\" color=\"" << color << "\"];\n"; m_processedNodes[node] = true; } From a8801459a90f458f22f13bcda089791c2fad3286 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 11 May 2014 23:57:31 +0700 Subject: [PATCH 055/290] Adds test graph generation code Issue: #32 --- src/main.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7fb5271..f5c2a6e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,20 @@ #include #endif +#include + +void testControlGraph(Image* image) { + TClass* objectClass = image->getGlobal("Object"); + TMethod* isKindOfMethod = objectClass->methods->find("isKindOf:"); + + st::ParsedMethod parsedMethod(isKindOfMethod); + st::ControlGraph controlGraph(&parsedMethod); + controlGraph.buildGraph(); + + ControlGraphVisualizer visualizer(&controlGraph, "graph.dot"); + visualizer.run(); +} + int main(int argc, char **argv) { args llstArgs; @@ -70,10 +84,12 @@ int main(int argc, char **argv) { std::auto_ptr smalltalkImage(new Image(memoryManager.get())); smalltalkImage->loadImage(llstArgs.imagePath); - { - Image::ImageWriter writer; - writer.setGlobals(globals).writeTo("../image/MySmalltalkImage.image"); - } + testControlGraph(smalltalkImage.get()); + +// { +// Image::ImageWriter writer; +// writer.setGlobals(globals).writeTo("../image/MySmalltalkImage.image"); +// } SmalltalkVM vm(smalltalkImage.get(), memoryManager.get()); // Creating completion database and filling it with info From c1cc79b951505497c8fbe5b9330a236467f19a92 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 15 May 2014 21:02:30 +0700 Subject: [PATCH 056/290] Restores PushBlock instruction processing Issue: #32 --- src/ParsedBytecode.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 625bb90..13ba5d9 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -30,7 +30,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Nested blocks are registered in the // container method, not the outer block. std::printf("%.4u : Parsing smalltalk block in interval [%u:%u)\n", currentBytePointer, startOffset, stopOffset); - //parseBlock(startOffset, stopOffset); + parseBlock(startOffset, stopOffset); // Skipping the nested block's bytecodes decoder.setBytePointer(stopOffset); @@ -107,12 +107,6 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Fetching instruction and appending it to the current basic block TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); - // Skipping nested smalltalk block bytecodes - if (instruction.getOpcode() == opcode::pushBlock) { - decoder.setBytePointer(instruction.getExtra()); - continue; - } - // Skipping dead code if (terminatorEncoded) { std::printf("%.4u : skipping dead code\n", currentBytePointer); @@ -121,6 +115,12 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { currentBasicBlock->append(instruction); } + // Skipping nested smalltalk block bytecodes + if (instruction.getOpcode() == opcode::pushBlock) { + decoder.setBytePointer(instruction.getExtra()); + continue; + } + if (instruction.isTerminator()) { std::printf("%.4u : terminator encoded\n", currentBytePointer); terminatorEncoded = true; From 0f297416c511a25094d3e797e30b02dff5932d4d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 15 May 2014 23:56:27 +0700 Subject: [PATCH 057/290] Adds comparison operator for TSmalltalkInstruction and isValueX accessors Issue: #32 --- include/instructions.h | 10 +++++++++ src/TSmalltalkInstruction.cpp | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/include/instructions.h b/include/instructions.h index 6f67a0b..08d21ec 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -40,8 +40,18 @@ struct TSmalltalkInstruction { static_cast(m_argument) << 8 | static_cast(m_extra) << 16; } + + bool operator ==(const TSmalltalkInstruction& instruction) const { + return + m_opcode == instruction.m_opcode && + m_argument == instruction.m_argument && + m_extra == instruction.m_extra; + } + bool isTerminator() const; bool isBranch() const; + bool isValueProvider() const; + bool isValueConsumer() const; std::string toString() const; private: TOpcode m_opcode; diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 21a7367..e70cbe2 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -36,6 +36,48 @@ bool st::TSmalltalkInstruction::isBranch() const } } +bool st::TSmalltalkInstruction::isValueProvider() const { + switch (m_opcode) { + case opcode::pushInstance: + case opcode::pushArgument: + case opcode::pushTemporary: + case opcode::pushLiteral: + case opcode::pushBlock: + case opcode::pushConstant: + case opcode::markArguments: + case opcode::sendMessage: + case opcode::sendUnary: + case opcode::sendBinary: + return true; + + case opcode::assignTemporary: + case opcode::assignInstance: + case opcode::doPrimitive: // ? + return false; + + case opcode::doSpecial: + switch (m_argument) { + case special::duplicate: + case special::sendToSuper: + return true; + + case special::selfReturn: + case special::stackReturn: + case special::blockReturn: + case special::popTop: + case special::branch: + case special::branchIfTrue: + case special::branchIfFalse: + return false; + } + } +} + +bool st::TSmalltalkInstruction::isValueConsumer() const { + assert(false); // TODO + return false; +} + std::string st::TSmalltalkInstruction::toString() const { std::ostringstream ss; From 94c92e6a3b72eb2cefaec4b6179211a82bd497ae Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 15 May 2014 23:57:42 +0700 Subject: [PATCH 058/290] Adds consumer analysis part of control graph optimizer Issue: #32 --- src/ControlGraph.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 791b30e..c5f7997 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -354,6 +354,70 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar return result; } +class GraphOptimizer : public NodeVisitor { +public: + GraphOptimizer(ControlGraph* graph) : NodeVisitor(graph) {} + + virtual bool visitDomain(ControlDomain& domain) { + m_currentDomain = &domain; + return NodeVisitor::visitDomain(domain); + } + + virtual bool visitNode(ControlNode& node) { + // If node pushes value on the stack but this value is not consumed + // by another node, or the only consumer is a popTop instruction + // then we may remove such node (or a node pair) + + if (InstructionNode* instruction = node.cast()) { + if (! instruction->getInstruction().isValueProvider()) + return NodeVisitor::visitNode(node); + + TNodeSet consumers; + if (!getConsumers(instruction, consumers)) { + std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); + m_nodesToDelete.push_back(instruction); + } else if (consumers.size() == 1) { + if (InstructionNode* consumer = (*consumers.begin())->cast()) { + const TSmalltalkInstruction& consumerInstruction = consumer->getInstruction(); + if (consumerInstruction == TSmalltalkInstruction(opcode::doSpecial, special::popTop)) { + std::printf("GraphOptimizer::visitNode : node %u is consumed only by popTop %u and may be removed\n", + instruction->getIndex(), + consumer->getIndex()); + + m_nodesToDelete.push_back(consumer); + m_nodesToDelete.push_back(instruction); + } + } + } + } + + return NodeVisitor::visitNode(node); + } + +private: + bool getConsumers(InstructionNode* node, TNodeSet& consumers) { + consumers.clear(); + + const TNodeSet& outEdges = node->getOutEdges(); + TNodeSet::iterator iEdge = outEdges.begin(); + for (; iEdge != outEdges.end(); ++iEdge) { + if (InstructionNode* instruction = (*iEdge)->cast()) { + const std::size_t argsCount = instruction->getArgumentsCount(); + for (std::size_t index = 0; index < argsCount; index++) { + if (instruction->getArgument(index) == node) + consumers.insert(instruction); + } + } + } + + return !consumers.empty(); + } + +private: + TNodeList m_nodesToDelete; + ControlDomain* m_currentDomain; +}; + void ControlGraph::buildGraph() { // Iterating through basic blocks of parsed method and constructing node domains @@ -368,4 +432,9 @@ void ControlGraph::buildGraph() std::printf("Phase 2. Linking control graph\n"); GraphLinker linker(this); linker.run(); + + // Optimizing graph by removing stalled nodes and merging linear branch sequences + std::printf("Phase 3. Optimizing control graph\n"); + GraphOptimizer optimizer(this); + optimizer.run(); } From f4e3d5da6345ab807fc880dd969c7b3dcd88bb73 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 16 May 2014 13:47:18 +0700 Subject: [PATCH 059/290] Adds specifier for const pointers Issue: #32 --- src/ControlGraph.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index c5f7997..434c97a 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -35,7 +35,7 @@ class GraphConstructor : public InstructionVisitor { virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { // Initializing instruction node - InstructionNode* newNode = m_graph->newNode(); + InstructionNode* const newNode = m_graph->newNode(); newNode->setInstruction(instruction); newNode->setDomain(m_currentDomain); m_currentDomain->addNode(newNode); @@ -257,7 +257,7 @@ class GraphLinker : public NodeVisitor { m_nodeToLink = 0; } - InstructionNode* instruction = node.cast(); + InstructionNode* const instruction = node.cast(); if (instruction && instruction->getInstruction().isTerminator()) return; // terminator nodes will take care of themselves @@ -301,7 +301,7 @@ class GraphLinker : public NodeVisitor { } void processRequest(ControlDomain* domain, std::size_t argumentIndex, const ControlDomain::TArgumentRequest& request) { - ControlNode* node = getRequestedNode(domain, argumentIndex); + ControlNode* const node = getRequestedNode(domain, argumentIndex); std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", node->getIndex(), request.requestingNode->getIndex()); node->addEdge(request.requestingNode); @@ -332,7 +332,7 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar if (!refererStackSize || argumentIndex > refererStackSize - 1) { // Referer block do not have enough values on it's stack. // We need to go deeper and process it's referers in turn. - ControlNode* refererValue = getRequestedNode(refererDomain, argumentIndex); + ControlNode* const refererValue = getRequestedNode(refererDomain, argumentIndex); if (singleReferer) result = refererValue; @@ -340,8 +340,8 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar refererValue->addEdge(result); } else { - const std::size_t valueIndex = refererStackSize - 1 - argumentIndex; - ControlNode* stackValue = refererStack[valueIndex]; + const std::size_t valueIndex = refererStackSize - 1 - argumentIndex; + ControlNode* const stackValue = refererStack[valueIndex]; if (singleReferer) result = stackValue; @@ -368,16 +368,16 @@ class GraphOptimizer : public NodeVisitor { // by another node, or the only consumer is a popTop instruction // then we may remove such node (or a node pair) - if (InstructionNode* instruction = node.cast()) { + if (InstructionNode* const instruction = node.cast()) { if (! instruction->getInstruction().isValueProvider()) return NodeVisitor::visitNode(node); TNodeSet consumers; - if (!getConsumers(instruction, consumers)) { + if (! getConsumers(instruction, consumers)) { std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); m_nodesToDelete.push_back(instruction); } else if (consumers.size() == 1) { - if (InstructionNode* consumer = (*consumers.begin())->cast()) { + if (InstructionNode* const consumer = (*consumers.begin())->cast()) { const TSmalltalkInstruction& consumerInstruction = consumer->getInstruction(); if (consumerInstruction == TSmalltalkInstruction(opcode::doSpecial, special::popTop)) { std::printf("GraphOptimizer::visitNode : node %u is consumed only by popTop %u and may be removed\n", @@ -401,7 +401,7 @@ class GraphOptimizer : public NodeVisitor { const TNodeSet& outEdges = node->getOutEdges(); TNodeSet::iterator iEdge = outEdges.begin(); for (; iEdge != outEdges.end(); ++iEdge) { - if (InstructionNode* instruction = (*iEdge)->cast()) { + if (InstructionNode* const instruction = (*iEdge)->cast()) { const std::size_t argsCount = instruction->getArgumentsCount(); for (std::size_t index = 0; index < argsCount; index++) { if (instruction->getArgument(index) == node) From ac745465d99fb1fe18a6e1ea9ac0ffaf58fe8ebb Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 17 May 2014 21:38:24 +0700 Subject: [PATCH 060/290] Adds xxxVisited() functions for node and domain visitors This is very convenient when we need to write a visitor that should perform some action right after last element had been visited. Of course, these functions may be overridden in descendants of complex visitors like NodeVisitor to track finish event from each level of hierarchy. Note however, that some handlers may be called several times depending on the actual hierarchy. For example, descendant of NodeVisitor will have it's nodesVisited() handler called after each of domain is processed. Still, domainsVisited() will be called only once, because it is a top of hierarchy. The default implementation of handler functions does nothing. Issue: #32 --- include/analysis.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 8c592e3..7368bf0 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -342,16 +342,21 @@ class DomainVisitor { virtual ~DomainVisitor() { } virtual bool visitDomain(ControlDomain& domain) { return true; } + virtual void domainsVisited() { } void run() { ControlGraph::iterator iDomain = m_graph->begin(); const ControlGraph::iterator iEnd = m_graph->end(); - while (iDomain != iEnd) { - if (! visitDomain(** iDomain)) - break; + if (iDomain != iEnd) { + while (iDomain != iEnd) { + if (! visitDomain(** iDomain)) + break; + + ++iDomain; + } - ++iDomain; + domainsVisited(); } } @@ -363,17 +368,22 @@ class NodeVisitor : public DomainVisitor { public: NodeVisitor(ControlGraph* graph) : DomainVisitor(graph) { } virtual bool visitNode(ControlNode& node) { return true; } + virtual void nodesVisited() { } protected: virtual bool visitDomain(ControlDomain& domain) { ControlDomain::iterator iNode = domain.begin(); const ControlDomain::iterator iEnd = domain.end(); - while (iNode != iEnd) { - if (! visitNode(** iNode)) - return false; + if (iNode != iEnd) { + while (iNode != iEnd) { + if (! visitNode(** iNode)) + return false; + + ++iNode; + } - ++iNode; + nodesVisited(); } return true; From e6934f528bcc843bef27e3a0f245c23c8b7f5e97 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 17 May 2014 21:53:54 +0700 Subject: [PATCH 061/290] Adds isTrivial() check to TSmalltalkInstruction Instructions may be divided into trivial and non-trivial. Trivial instructions have no side effects. Their action is very straightforward and obvious. For example, all push instructions are considered to be trivial. They do what they do and nothing more. Chains of trivial instructions that appear to be dead code or considered to be redundant may easily be removed without affecting the method behavior. For example pair of `PushXXX' and `Special popTop' instructions may be removed with no worry whereas SendMessage innstruction may not even if it's result is not consumed by instructions except `Special popTop'. Note: Strictly speaking, MarkArguments so as PushBlock are not trivial. However, their use patterns within method code are very well known. We treat them as trivial for the sake of code clarity that handles them. Issue: #32 --- include/instructions.h | 2 ++ src/TSmalltalkInstruction.cpp | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/instructions.h b/include/instructions.h index 08d21ec..582ec42 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -48,10 +48,12 @@ struct TSmalltalkInstruction { m_extra == instruction.m_extra; } + bool isTrivial() const; bool isTerminator() const; bool isBranch() const; bool isValueProvider() const; bool isValueConsumer() const; + std::string toString() const; private: TOpcode m_opcode; diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index e70cbe2..2046c9a 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -70,6 +70,27 @@ bool st::TSmalltalkInstruction::isValueProvider() const { case special::branchIfFalse: return false; } +} + +bool st::TSmalltalkInstruction::isTrivial() const { + switch (m_opcode) { + case opcode::pushInstance: + case opcode::pushArgument: + case opcode::pushTemporary: + case opcode::pushLiteral: + case opcode::pushConstant: + case opcode::pushBlock: + case opcode::markArguments: + return true; + + case opcode::doSpecial: + switch (m_argument) { + case special::duplicate: + return true; + } + + default: + return false; } } From 5169a652ac96bdce60489364697e0a5fa1977b9c Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 17 May 2014 22:00:17 +0700 Subject: [PATCH 062/290] Fixes GraphOptimizer not to remove non-trivial instructions Some instructions like SendMessage may have their value ignored. Still, we may not remove them because operation have side effects. Only trivial instructions may be removed easily, others should be proven to be dead or redundant in order to do so. Issue: #32 --- src/ControlGraph.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 434c97a..9d022d0 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -369,13 +369,14 @@ class GraphOptimizer : public NodeVisitor { // then we may remove such node (or a node pair) if (InstructionNode* const instruction = node.cast()) { - if (! instruction->getInstruction().isValueProvider()) + const TSmalltalkInstruction& nodeInstruction = instruction->getInstruction(); + if (!nodeInstruction.isTrivial() || !nodeInstruction.isValueProvider()) return NodeVisitor::visitNode(node); TNodeSet consumers; if (! getConsumers(instruction, consumers)) { std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); - m_nodesToDelete.push_back(instruction); + m_nodesToRemove.push_back(instruction); } else if (consumers.size() == 1) { if (InstructionNode* const consumer = (*consumers.begin())->cast()) { const TSmalltalkInstruction& consumerInstruction = consumer->getInstruction(); @@ -384,8 +385,8 @@ class GraphOptimizer : public NodeVisitor { instruction->getIndex(), consumer->getIndex()); - m_nodesToDelete.push_back(consumer); - m_nodesToDelete.push_back(instruction); + m_nodesToRemove.push_back(consumer); + m_nodesToRemove.push_back(instruction); } } } @@ -394,6 +395,11 @@ class GraphOptimizer : public NodeVisitor { return NodeVisitor::visitNode(node); } + virtual void domainsVisited() { + TNodeList::iterator iNode = m_nodesToRemove.begin(); + for (; iNode != m_nodesToRemove.end(); ++iNode) + removeNode(*iNode); + } private: bool getConsumers(InstructionNode* node, TNodeSet& consumers) { consumers.clear(); @@ -413,8 +419,12 @@ class GraphOptimizer : public NodeVisitor { return !consumers.empty(); } + void removeNode(ControlNode* node) { + // TODO + } + private: - TNodeList m_nodesToDelete; + TNodeList m_nodesToRemove; ControlDomain* m_currentDomain; }; From a9eaaa032eac1af46d5d269f9409d77c3e769f46 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 17 May 2014 22:01:19 +0700 Subject: [PATCH 063/290] Handles opcode::extended in TSmalltalkInstruction::isValueProvider() Issue: #32 --- src/TSmalltalkInstruction.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 2046c9a..34ff81e 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -70,6 +70,12 @@ bool st::TSmalltalkInstruction::isValueProvider() const { case special::branchIfFalse: return false; } + + case opcode::extended: + assert(false); + } + + return false; } bool st::TSmalltalkInstruction::isTrivial() const { From 6f97232d0a44bde273d6ad2d75d150fdcb4fc0d4 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 17 May 2014 22:01:46 +0700 Subject: [PATCH 064/290] Adds some more coloring to the GraphVisualizer Issue: #32 --- src/ControlGraphVisualizer.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 459a8c6..349e1e5 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -83,17 +83,26 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { color = "green"; break; - case st::ControlNode::ntInstruction: - label = node->cast()->getInstruction().toString(); - if (node->cast()->getInstruction().isTerminator()) + case st::ControlNode::ntInstruction: { + st::InstructionNode* instruction = node->cast(); + label = instruction->getInstruction().toString(); + + const bool isTerminator = instruction->getInstruction().isTerminator(); + const bool isEntryPoint = (instruction == instruction->getDomain()->getEntryPoint()); + + if (isTerminator && isEntryPoint) + color = "green3;red"; + else if (isEntryPoint) + color = "green3"; + else if (isTerminator) color = "red"; - break; + } break; default: ; } - m_stream << "\t\t " << node->getIndex() << "[shape=box label=\"" << node->getIndex() << " : " << label << "\" color=\"" << color << "\"];\n"; + m_stream << "\t\t" << node->getIndex() << " [shape=box label=\"" << node->getDomain()->getBasicBlock()->getOffset() << "." << node->getIndex() << " : " << label << "\" color=\"" << color << "\"];\n"; // m_stream << "\t\t " << node->getIndex() << "[label=\"" << node->getIndex() /*<< " : " << label */<< "\" color=\"" << color << "\"];\n"; m_processedNodes[node] = true; } From 41b52b7a7be767e35ba3f65ca36741b885b33016 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 18 May 2014 11:01:07 +0700 Subject: [PATCH 065/290] Refactors MethodCompiler::writeFunctionBody() to use NodeVisitor Issue: #32 --- include/jit.h | 15 ++++---- src/MethodCompiler.cpp | 80 +++++++++++++++++------------------------- 2 files changed, 42 insertions(+), 53 deletions(-) diff --git a/include/jit.h b/include/jit.h index cf634aa..9f497ab 100644 --- a/include/jit.h +++ b/include/jit.h @@ -34,6 +34,7 @@ #include #include "vm.h" +#include "analysis.h" #include @@ -220,8 +221,9 @@ class MethodCompiler { // This structure contains working data which is // used during the compilation process. struct TJITContext { - st::ParsedMethod parsedMethod; // Parsed smalltalk method we're currently processing - st::TSmalltalkInstruction instruction; // currently processed instruction + st::ParsedMethod parsedMethod; // Parsed smalltalk method we're currently processing + st::ControlGraph controlGraph; + st::InstructionNode* currentNode; TMethod* method; // Smalltalk method we're currently processing uint16_t bytePointer; @@ -253,11 +255,12 @@ class MethodCompiler { void pushValue(TStackValue* value); TJITContext(MethodCompiler* compiler, TMethod* method) - : parsedMethod(method), instruction(opcode::extended), method(method), bytePointer(0), function(0), builder(0), - preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), - contextHolder(0), selfHolder(0) + : parsedMethod(method), controlGraph(&parsedMethod), currentNode(0), + method(method), bytePointer(0), function(0), builder(0), + preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), + contextHolder(0), selfHolder(0) { - + controlGraph.buildGraph(); }; ~TJITContext() { diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index b383f14..3c9b42d 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -485,39 +485,25 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF void MethodCompiler::writeFunctionBody(TJITContext& jit, uint32_t byteCount /*= 0*/) { - class Visitor : public st::InstructionVisitor { + class Visitor : public st::NodeVisitor { public: - Visitor(TJITContext& jit) : InstructionVisitor(&jit.parsedMethod), m_jit(jit) { } + Visitor(TJITContext& jit) : st::NodeVisitor(&jit.controlGraph), m_jit(jit) { } private: - virtual bool visitBlock(st::BasicBlock& basicBlock) { - // A new basic block had just been started - // Switching context to it and linking blocks if necessary - - llvm::BasicBlock* newBlock = m_jit.compiler->m_targetToBlockMap[basicBlock.getOffset()]; - - //If the current BB does not have a terminator, we create a br to newBlock - if (! m_jit.builder->GetInsertBlock()->getTerminator() ) { - m_jit.builder->CreateBr(newBlock); // Linking current block to a new one - - // Inserting current block as a referer to the newly created one - // Popping the value may result in popping the referer's stack - // or even generation of phi function if there are several referers - m_jit.basicBlockContexts[newBlock].referers.insert(m_jit.builder->GetInsertBlock()); - } + virtual bool visitDomain(st::ControlDomain& domain) { + llvm::BasicBlock* newBlock = m_jit.compiler->m_targetToBlockMap[domain.getBasicBlock()->getOffset()]; newBlock->moveAfter(m_jit.builder->GetInsertBlock()); // for a pretty sequenced BB output m_jit.builder->SetInsertPoint(newBlock); - return InstructionVisitor::visitBlock(basicBlock); + return NodeVisitor::visitDomain(domain); } - virtual bool visitInstruction(const st::TSmalltalkInstruction& instruction) { - // Processing instruction - m_jit.instruction = instruction; + virtual bool visitNode(st::ControlNode& node) { + m_jit.currentNode = (&node)->cast(); m_jit.compiler->writeInstruction(m_jit); - return true; + return NodeVisitor::visitNode(node); } TJITContext& m_jit; @@ -528,7 +514,7 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit, uint32_t byteCount /*= } void MethodCompiler::writeInstruction(TJITContext& jit) { - switch (jit.instruction.getOpcode()) { + switch (jit.currentNode->getInstruction().getOpcode()) { // TODO Boundary checks against container's real size case opcode::pushInstance: doPushInstance(jit); break; case opcode::pushArgument: doPushArgument(jit); break; @@ -557,7 +543,7 @@ void MethodCompiler::writeInstruction(TJITContext& jit) { default: std::fprintf(stderr, "JIT: Invalid opcode %d at offset %d in method %s\n", - jit.instruction.getOpcode(), jit.bytePointer, jit.method->name->toString().c_str()); + jit.currentNode->getInstruction().getOpcode(), jit.bytePointer, jit.method->name->toString().c_str()); } } @@ -598,31 +584,31 @@ void MethodCompiler::doPushInstance(TJITContext& jit) // Self is interpreted as object array. // Array elements are instance variables - uint8_t index = jit.instruction.getArgument(); + uint8_t index = jit.currentNode->getInstruction().getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadInstance, index)); } void MethodCompiler::doPushArgument(TJITContext& jit) { - uint8_t index = jit.instruction.getArgument(); + uint8_t index = jit.currentNode->getInstruction().getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadArgument, index)); } void MethodCompiler::doPushTemporary(TJITContext& jit) { - uint8_t index = jit.instruction.getArgument(); + uint8_t index = jit.currentNode->getInstruction().getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadTemporary, index)); } void MethodCompiler::doPushLiteral(TJITContext& jit) { - uint8_t index = jit.instruction.getArgument(); + uint8_t index = jit.currentNode->getInstruction().getArgument(); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadLiteral, index)); } void MethodCompiler::doPushConstant(TJITContext& jit) { - const uint32_t constant = jit.instruction.getArgument(); + const uint32_t constant = jit.currentNode->getInstruction().getArgument(); Value* constantValue = 0; switch (constant) { @@ -657,7 +643,7 @@ void MethodCompiler::doPushConstant(TJITContext& jit) void MethodCompiler::doPushBlock(TJITContext& jit) { - const uint16_t newBytePointer = jit.instruction.getExtra(); + const uint16_t newBytePointer = jit.currentNode->getInstruction().getExtra(); TJITContext blockContext(this, jit.method); blockContext.bytePointer = jit.bytePointer; @@ -703,7 +689,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) // Create block object and fill it with context information Value* args[] = { jit.getCurrentContext(), // creatingContext - jit.builder->getInt8(jit.instruction.getArgument()), // arg offset + jit.builder->getInt8(jit.currentNode->getInstruction().getArgument()), // arg offset jit.builder->getInt16(blockOffset) // bytePointer }; Value* blockObject = jit.builder->CreateCall(m_runtimeAPI.createBlock, args); @@ -717,7 +703,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) void MethodCompiler::doAssignTemporary(TJITContext& jit) { - uint8_t index = jit.instruction.getArgument(); + uint8_t index = jit.currentNode->getInstruction().getArgument(); Value* value = jit.lastValue(); IRBuilder<>& builder = * jit.builder; @@ -729,7 +715,7 @@ void MethodCompiler::doAssignTemporary(TJITContext& jit) void MethodCompiler::doAssignInstance(TJITContext& jit) { - uint8_t index = jit.instruction.getArgument(); + uint8_t index = jit.currentNode->getInstruction().getArgument(); Value* value = jit.lastValue(); IRBuilder<>& builder = * jit.builder; @@ -744,7 +730,7 @@ void MethodCompiler::doAssignInstance(TJITContext& jit) void MethodCompiler::doMarkArguments(TJITContext& jit) { // Here we need to create the arguments array from the values on the stack - uint8_t argumentsCount = jit.instruction.getArgument(); + uint8_t argumentsCount = jit.currentNode->getInstruction().getArgument(); // FIXME Probably we may unroll the arguments array and pass the values directly. // However, in some cases this may lead to additional architectural problems. @@ -768,12 +754,12 @@ void MethodCompiler::doSendUnary(TJITContext& jit) Value* value = jit.popValue(); Value* condition = 0; - switch ( static_cast(jit.instruction.getArgument()) ) { + switch ( static_cast(jit.currentNode->getInstruction().getArgument()) ) { case unaryBuiltIns::isNil: condition = jit.builder->CreateICmpEQ(value, m_globals.nilObject, "isNil."); break; case unaryBuiltIns::notNil: condition = jit.builder->CreateICmpNE(value, m_globals.nilObject, "notNil."); break; default: - std::fprintf(stderr, "JIT: Invalid opcode %d passed to sendUnary\n", jit.instruction.getArgument()); + std::fprintf(stderr, "JIT: Invalid opcode %d passed to sendUnary\n", jit.currentNode->getInstruction().getArgument()); } Value* result = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); @@ -783,7 +769,7 @@ void MethodCompiler::doSendUnary(TJITContext& jit) void MethodCompiler::doSendBinary(TJITContext& jit) { // 0, 1 or 2 for '<', '<=' or '+' respectively - binaryBuiltIns::Operator opcode = static_cast(jit.instruction.getArgument()); + binaryBuiltIns::Operator opcode = static_cast(jit.currentNode->getInstruction().getArgument()); Value* rightValue = jit.popValue(); Value* leftValue = jit.popValue(); @@ -859,7 +845,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) Value* argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); Value* sendMessageArgs[] = { jit.getCurrentContext(), // calling context - m_globals.binarySelectors[jit.instruction.getArgument()], + m_globals.binarySelectors[jit.currentNode->getInstruction().getArgument()], argumentsArray, // default receiver class @@ -900,11 +886,11 @@ void MethodCompiler::doSendMessage(TJITContext& jit) Value* arguments = jit.popValue(); // First of all we need to get the actual message selector - Value* selectorObject = jit.getLiteral(jit.instruction.getArgument()); + Value* selectorObject = jit.getLiteral(jit.currentNode->getInstruction().getArgument()); Value* messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); std::ostringstream ss; - ss << "#" << jit.method->literals->getField(jit.instruction.getArgument())->toString() << "."; + ss << "#" << jit.method->literals->getField(jit.currentNode->getInstruction().getArgument())->toString() << "."; messageSelector->setName(ss.str()); // Forming a message parameters @@ -943,7 +929,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) void MethodCompiler::doSpecial(TJITContext& jit) { - const uint8_t opcode = jit.instruction.getArgument(); + const uint8_t opcode = jit.currentNode->getInstruction().getArgument(); BasicBlock::iterator iPreviousInst = jit.builder->GetInsertPoint(); if (iPreviousInst != jit.builder->GetInsertBlock()->begin()) @@ -1003,7 +989,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::branch: { // Loading branch target bytecode offset - uint32_t targetOffset = jit.instruction.getExtra(); + uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); if (!iPreviousInst->isTerminator()) { // Finding appropriate branch target @@ -1019,7 +1005,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::branchIfTrue: case special::branchIfFalse: { // Loading branch target bytecode offset - uint32_t targetOffset = jit.instruction.getExtra(); + uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); if (!iPreviousInst->isTerminator()) { // Finding appropriate branch target @@ -1049,7 +1035,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) Value* argsObject = jit.popValue(); Value* arguments = jit.builder->CreateBitCast(argsObject, m_baseTypes.objectArray->getPointerTo()); - uint32_t literalIndex = jit.instruction.getExtra(); + uint32_t literalIndex = jit.currentNode->getInstruction().getExtra(); Value* selectorObject = jit.getLiteral(literalIndex); Value* messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); @@ -1078,7 +1064,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) void MethodCompiler::doPrimitive(TJITContext& jit) { - uint32_t opcode = jit.instruction.getArgument(); + uint32_t opcode = jit.currentNode->getInstruction().getArgument(); Value* primitiveResult = 0; Value* primitiveFailed = jit.builder->getFalse(); @@ -1243,7 +1229,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, Value* object = jit.popValue(); Value* block = jit.builder->CreateBitCast(object, m_baseTypes.block->getPointerTo()); - int32_t argCount = jit.instruction.getArgument() - 1; + int32_t argCount = jit.currentNode->getInstruction().getArgument() - 1; Value* blockAsContext = jit.builder->CreateBitCast(block, m_baseTypes.context->getPointerTo()); Function* getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); @@ -1459,7 +1445,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, default: { // Here we need to create the arguments array from the values on the stack - uint8_t argumentsCount = jit.instruction.getArgument(); + uint8_t argumentsCount = jit.currentNode->getInstruction().getArgument(); Value* argumentsObject = createArray(jit, argumentsCount); // Filling object with contents From 4734ddf8a6d87c7ba4db15153fd348d471cf91a2 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 18 May 2014 13:01:48 +0700 Subject: [PATCH 066/290] Uses node index instead of bytePointer in call sites Issue: #32 --- src/MethodCompiler.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 3c9b42d..3c7661f 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -542,8 +542,9 @@ void MethodCompiler::writeInstruction(TJITContext& jit) { case 0: break; default: - std::fprintf(stderr, "JIT: Invalid opcode %d at offset %d in method %s\n", - jit.currentNode->getInstruction().getOpcode(), jit.bytePointer, jit.method->name->toString().c_str()); + std::fprintf(stderr, "JIT: Invalid opcode %d at node %d in method %s\n", + jit.currentNode->getInstruction().getOpcode(), + jit.currentNode->getIndex(), jit.method->name->toString().c_str()); } } @@ -649,7 +650,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) blockContext.bytePointer = jit.bytePointer; // Creating block function named Class>>method@offset - const uint16_t blockOffset = jit.bytePointer; + const uint16_t blockOffset = jit.currentNode->getIndex(); //jit.bytePointer; std::ostringstream ss; ss << jit.method->klass->name->toString() + ">>" + jit.method->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); @@ -850,9 +851,9 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // default receiver class ConstantPointerNull::get(m_baseTypes.klass->getPointerTo()), //inttoptr 0 works fine too - /*jit.builder->getInt32(m_callSiteIndex) // */jit.builder->getInt32(jit.bytePointer) // call site offset + jit.builder->getInt32(jit.currentNode->getIndex()) // call site index }; - m_callSiteIndexToOffset[m_callSiteIndex++] = jit.bytePointer; + m_callSiteIndexToOffset[m_callSiteIndex++] = jit.currentNode->getIndex(); // Now performing a message call Value* sendMessageResult = 0; @@ -901,9 +902,9 @@ void MethodCompiler::doSendMessage(TJITContext& jit) // default receiver class ConstantPointerNull::get(m_baseTypes.klass->getPointerTo()), - /*jit.builder->getInt32(m_callSiteIndex) //*/ jit.builder->getInt32(jit.bytePointer) // call site offset + jit.builder->getInt32(jit.currentNode->getIndex()) // call site index }; - m_callSiteIndexToOffset[m_callSiteIndex++] = jit.bytePointer; + m_callSiteIndexToOffset[m_callSiteIndex++] = jit.currentNode->getIndex(); Value* result = 0; if (jit.methodHasBlockReturn) { @@ -1048,9 +1049,9 @@ void MethodCompiler::doSpecial(TJITContext& jit) messageSelector, // selector arguments, // message arguments parentClass, // receiver class - /*jit.builder->getInt32(m_callSiteIndex) //*/ jit.builder->getInt32(jit.bytePointer) // call site offset + jit.builder->getInt32(jit.currentNode->getIndex()) // call site index }; - m_callSiteIndexToOffset[m_callSiteIndex++] = jit.bytePointer; + m_callSiteIndexToOffset[m_callSiteIndex++] = jit.currentNode->getIndex(); Value* result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); Value* resultHolder = protectPointer(jit, result); @@ -1419,9 +1420,9 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, args, // default receiver class ConstantPointerNull::get(m_baseTypes.klass->getPointerTo()), //inttoptr 0 works fine too - /*jit.builder->getInt32(m_callSiteIndex) //*/ jit.builder->getInt32(jit.bytePointer) // call site offset + jit.builder->getInt32(jit.currentNode->getIndex()) // call site index }; - m_callSiteIndexToOffset[m_callSiteIndex++] = jit.bytePointer; + m_callSiteIndexToOffset[m_callSiteIndex++] = jit.currentNode->getIndex(); // Now performing a message call Value* sendMessageResult = 0; From 58311e0e3a2dbadb84038e040f7db069fa0b82cf Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 18 May 2014 13:08:04 +0700 Subject: [PATCH 067/290] Gets rid of bytePointer in TJITContext and compiler Note: PushBlock instruction will not work correctly Issue: #32 --- include/jit.h | 5 ++--- src/MethodCompiler.cpp | 20 ++++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/include/jit.h b/include/jit.h index 9f497ab..89f17cd 100644 --- a/include/jit.h +++ b/include/jit.h @@ -226,7 +226,6 @@ class MethodCompiler { st::InstructionNode* currentNode; TMethod* method; // Smalltalk method we're currently processing - uint16_t bytePointer; llvm::Function* function; // LLVM function that is created based on method llvm::IRBuilder<>* builder; // Builder inserts instructions into basic blocks @@ -256,7 +255,7 @@ class MethodCompiler { TJITContext(MethodCompiler* compiler, TMethod* method) : parsedMethod(method), controlGraph(&parsedMethod), currentNode(0), - method(method), bytePointer(0), function(0), builder(0), + method(method), function(0), builder(0), preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), contextHolder(0), selfHolder(0) { @@ -303,7 +302,7 @@ class MethodCompiler { llvm::Value* protectPointer(TJITContext& jit, llvm::Value* value); void writePreamble(TJITContext& jit, bool isBlock = false); - void writeFunctionBody(TJITContext& jit, uint32_t byteCount = 0); + void writeFunctionBody(TJITContext& jit); void writeInstruction(TJITContext& jit); void writeLandingPad(TJITContext& jit); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 3c7661f..5b7d7a1 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -483,7 +483,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF return jit.function; } -void MethodCompiler::writeFunctionBody(TJITContext& jit, uint32_t byteCount /*= 0*/) +void MethodCompiler::writeFunctionBody(TJITContext& jit) { class Visitor : public st::NodeVisitor { public: @@ -522,7 +522,7 @@ void MethodCompiler::writeInstruction(TJITContext& jit) { case opcode::pushLiteral: doPushLiteral(jit); break; case opcode::pushConstant: doPushConstant(jit); break; - case opcode::pushBlock: doPushBlock(jit); break; + case opcode::pushBlock: doPushBlock(jit); break; // FIXME case opcode::assignTemporary: doAssignTemporary(jit); break; case opcode::assignInstance: doAssignInstance(jit); break; @@ -644,15 +644,12 @@ void MethodCompiler::doPushConstant(TJITContext& jit) void MethodCompiler::doPushBlock(TJITContext& jit) { - const uint16_t newBytePointer = jit.currentNode->getInstruction().getExtra(); - - TJITContext blockContext(this, jit.method); - blockContext.bytePointer = jit.bytePointer; + TJITContext blockContext(this, jit.method); // FIXME // Creating block function named Class>>method@offset - const uint16_t blockOffset = jit.currentNode->getIndex(); //jit.bytePointer; + const uint16_t nodeIndex = jit.currentNode->getIndex(); std::ostringstream ss; - ss << jit.method->klass->name->toString() + ">>" + jit.method->name->toString() << "@" << blockOffset; + ss << jit.method->klass->name->toString() + ">>" + jit.method->name->toString() << "@" << nodeIndex; std::string blockFunctionName = ss.str(); std::vector blockParams; @@ -675,13 +672,13 @@ void MethodCompiler::doPushBlock(TJITContext& jit) blockContext.preamble = BasicBlock::Create(m_JITModule->getContext(), "blockPreamble", blockContext.function); blockContext.builder = new IRBuilder<>(blockContext.preamble); writePreamble(blockContext, /*isBlock*/ true); - scanForBranches(blockContext, newBytePointer - jit.bytePointer); + scanForBranches(blockContext); BasicBlock* blockBody = BasicBlock::Create(m_JITModule->getContext(), "blockBody", blockContext.function); blockContext.builder->CreateBr(blockBody); blockContext.builder->SetInsertPoint(blockBody); - writeFunctionBody(blockContext, newBytePointer - jit.bytePointer); + writeFunctionBody(blockContext); // Running optimization passes on a block function JITRuntime::Instance()->optimizeFunction(blockContext.function); @@ -691,12 +688,11 @@ void MethodCompiler::doPushBlock(TJITContext& jit) Value* args[] = { jit.getCurrentContext(), // creatingContext jit.builder->getInt8(jit.currentNode->getInstruction().getArgument()), // arg offset - jit.builder->getInt16(blockOffset) // bytePointer + jit.builder->getInt16(nodeIndex) }; Value* blockObject = jit.builder->CreateCall(m_runtimeAPI.createBlock, args); blockObject = jit.builder->CreateBitCast(blockObject, m_baseTypes.object->getPointerTo()); blockObject->setName("block."); - jit.bytePointer = newBytePointer; Value* blockHolder = protectPointer(jit, blockObject); jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, blockHolder)); From 3149ab57116cb7c9485dea532e1c204649c06e11 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 18 May 2014 17:31:05 +0700 Subject: [PATCH 068/290] Adds access to parsed blocks by their stop offset Issue: #32 --- include/instructions.h | 15 ++++++++++----- src/ParsedBlock.cpp | 2 +- src/ParsedMethod.cpp | 9 +++++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index 582ec42..7cd5035 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -244,19 +244,24 @@ class ParsedMethod : public ParsedBytecode { return 0; } + ParsedBlock* getParsedBlockByEndOffset(uint16_t endOffset) { + TOffsetToParsedBlockMap::iterator iBlock = m_endOffsetToParsedBlock.find(endOffset); + if (iBlock != m_endOffsetToParsedBlock.end()) + return iBlock->second; + else + return 0; + } + protected: virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); - - void addParsedBlock(uint16_t offset, ParsedBlock* parsedBlock) { - m_parsedBlocks.push_back(parsedBlock); - m_offsetToParsedBlock[offset] = parsedBlock; - } + void addParsedBlock(ParsedBlock* parsedBlock); protected: TParsedBlockList m_parsedBlocks; typedef std::map TOffsetToParsedBlockMap; TOffsetToParsedBlockMap m_offsetToParsedBlock; + TOffsetToParsedBlockMap m_endOffsetToParsedBlock; }; class ParsedBlock : public ParsedBytecode { diff --git a/src/ParsedBlock.cpp b/src/ParsedBlock.cpp index cc3f1d2..e079357 100644 --- a/src/ParsedBlock.cpp +++ b/src/ParsedBlock.cpp @@ -6,5 +6,5 @@ void ParsedBlock::parseBlock(uint16_t startOffset, uint16_t stopOffset) { ParsedMethod* container = getContainer(); ParsedBlock* nestedBlock = new ParsedBlock(container, startOffset, stopOffset); - container->addParsedBlock(startOffset, nestedBlock); + container->addParsedBlock(nestedBlock); } diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 4d5a210..9b66aca 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -9,6 +9,15 @@ void ParsedMethod::parseBlock(uint16_t startOffset, uint16_t stopOffset) { m_offsetToParsedBlock[startOffset] = parsedBlock; } +void ParsedMethod::addParsedBlock(ParsedBlock* parsedBlock) { + m_parsedBlocks.push_back(parsedBlock); + + const uint16_t startOffset = parsedBlock->getStartOffset(); + const uint16_t stopOffset = parsedBlock->getStopOffset(); + m_offsetToParsedBlock[startOffset] = parsedBlock; + m_endOffsetToParsedBlock[stopOffset] = parsedBlock; +} + ParsedMethod::~ParsedMethod() { for (TParsedBlockList::iterator iBlock = m_parsedBlocks.begin(), end = m_parsedBlocks.end(); iBlock != end; ++iBlock) From e839879b1639978b5559b0c66378608a5ae1093b Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 18 May 2014 17:31:42 +0700 Subject: [PATCH 069/290] Adds PushBlockNode This is a helper descendant of InstructionNode. It holds a pointer to corresponding ParsedBlock. Issue: #32 --- include/analysis.h | 17 +++++++++++++++++ src/ControlGraph.cpp | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 7368bf0..fadeaef 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -162,6 +162,20 @@ class InstructionNode : public ControlNode { TArgumentList m_arguments; }; +// PushBlockNode represents a single PushBlock instruction. +// It provides pointer to the associated ParsedBlock object. +// In all cases it may be safely treated as InstructionNode. +class PushBlockNode : public InstructionNode { +public: + PushBlockNode(uint32_t index) : InstructionNode(index) {} + + void setParsedBlock(ParsedBlock* block) { m_parsedBlock = block; } + ParsedBlock* getParsedBlock() const { return m_parsedBlock; } + +private: + ParsedBlock* m_parsedBlock; +}; + // Phi node act as a value aggregator from several domains. // When value is pushed on the stack in one basic block and // popped in another we say that actual values have a stack relation. @@ -336,6 +350,9 @@ template<> InstructionNode* ControlGraph::newNode(); template<> PhiNode* ControlGraph::newNode(); template<> TauNode* ControlGraph::newNode(); +template<> PushBlockNode* ControlNode::cast(); +template<> PushBlockNode* ControlGraph::newNode(); + class DomainVisitor { public: DomainVisitor(ControlGraph* graph) : m_graph(graph) { } diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 9d022d0..ed41358 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -20,6 +20,23 @@ template<> InstructionNode* ControlGraph::newNode() { return st template<> PhiNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntPhi)); } template<> TauNode* ControlGraph::newNode() { return static_cast(newNode(ControlNode::ntTau)); } +template<> PushBlockNode* ControlNode::cast() { + if (this->getNodeType() != ntInstruction) + return 0; + + InstructionNode* const node = static_cast(this); + if (node->getInstruction().getOpcode() != opcode::pushBlock) + return 0; + + return static_cast(this); +} + +template<> PushBlockNode* ControlGraph::newNode() { + PushBlockNode* node = new PushBlockNode(m_lastNodeIndex++); + m_nodes.push_back(node); + return static_cast(node); +} + class GraphConstructor : public InstructionVisitor { public: GraphConstructor(ControlGraph* graph) @@ -35,7 +52,7 @@ class GraphConstructor : public InstructionVisitor { virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { // Initializing instruction node - InstructionNode* const newNode = m_graph->newNode(); + InstructionNode* const newNode = createNode(instruction); newNode->setInstruction(instruction); newNode->setDomain(m_currentDomain); m_currentDomain->addNode(newNode); @@ -52,6 +69,7 @@ class GraphConstructor : public InstructionVisitor { } private: + InstructionNode* createNode(const TSmalltalkInstruction& instruction); void processNode(InstructionNode* node); void processSpecials(InstructionNode* node); void processPrimitives(InstructionNode* node); @@ -61,6 +79,14 @@ class GraphConstructor : public InstructionVisitor { bool m_skipStubInstructions; }; +InstructionNode* GraphConstructor::createNode(const TSmalltalkInstruction& instruction) +{ + if (instruction.getOpcode() == opcode::pushBlock) + return m_graph->newNode(); + else + return m_graph->newNode(); +} + void GraphConstructor::processNode(InstructionNode* node) { const TSmalltalkInstruction& instruction = node->getInstruction(); @@ -74,9 +100,14 @@ void GraphConstructor::processNode(InstructionNode* node) case opcode::pushArgument: case opcode::pushTemporary: // TODO Link with tau node case opcode::pushInstance: - case opcode::pushBlock: + case opcode::pushBlock: { + const uint16_t blockEndOffset = node->getInstruction().getExtra(); + ParsedMethod* const parsedMethod = m_graph->getParsedMethod(); + ParsedBlock* const parsedBlock = parsedMethod->getParsedBlockByEndOffset(blockEndOffset); + + node->cast()->setParsedBlock(parsedBlock); m_currentDomain->pushValue(node); - break; + } break; case opcode::assignTemporary: // TODO Link with tau node case opcode::assignInstance: From 7ac1a11e13eb732b9e0073d928e01bd4698704f1 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 25 May 2014 14:25:21 +0700 Subject: [PATCH 070/290] Adds ControlGraph constructor which accepts ParsedMethod ControlGraph is used not only for methods but also for inline blocks. We need to generalize graph logic to allow analyzing both ParsedMethod and ParsedBlock. Use getParsedBytecode() method to get the associated bytecode. Now getParsedMethod() return a container method for blocks and the method itself if it is a method's graph. Issue: #32 --- include/analysis.h | 15 ++++++++++++++- src/ControlGraph.cpp | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index fadeaef..d1ff230 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -268,9 +268,21 @@ class ControlDomain { class ControlGraph { public: - ControlGraph(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod), m_lastNodeIndex(0) { } + ControlGraph(ParsedMethod* parsedMethod) + : m_parsedMethod(parsedMethod), m_lastNodeIndex(0), m_parsedBlock(0) { } + + ControlGraph(ParsedMethod* parsedMethod, ParsedBlock* parsedBlock) + : m_parsedMethod(parsedMethod), m_parsedBlock(parsedBlock), m_lastNodeIndex(0) { } + ParsedMethod* getParsedMethod() const { return m_parsedMethod; } + ParsedBytecode* getParsedBytecode() const { + if (m_parsedBlock) + return m_parsedBlock; + else + return m_parsedMethod; + } + typedef TDomainSet::iterator iterator; iterator begin() { return m_domains.begin(); } iterator end() { return m_domains.end(); } @@ -332,6 +344,7 @@ class ControlGraph { private: ParsedMethod* m_parsedMethod; + ParsedBlock* m_parsedBlock; TDomainSet m_domains; typedef std::list TNodeList; diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index ed41358..e504e1f 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -40,7 +40,7 @@ template<> PushBlockNode* ControlGraph::newNode() { class GraphConstructor : public InstructionVisitor { public: GraphConstructor(ControlGraph* graph) - : InstructionVisitor(graph->getParsedMethod()), m_graph(graph) { } + : InstructionVisitor(graph->getParsedBytecode()), m_graph(graph) { } virtual bool visitBlock(BasicBlock& basicBlock) { m_currentDomain = m_graph->getDomainFor(&basicBlock); From 7f9c25093c5ff95525ea29799f265e569f8887e7 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 25 May 2014 14:28:09 +0700 Subject: [PATCH 071/290] Adds block context and processes PushBlock instruction Issue: #32 --- include/jit.h | 43 +++++++++++++++++++++++++++++++++--------- src/MethodCompiler.cpp | 28 +++++++++++++++------------ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/include/jit.h b/include/jit.h index 89f17cd..3bb2228 100644 --- a/include/jit.h +++ b/include/jit.h @@ -218,14 +218,12 @@ class MethodCompiler { TRefererSet referers; }; - // This structure contains working data which is - // used during the compilation process. struct TJITContext { - st::ParsedMethod parsedMethod; // Parsed smalltalk method we're currently processing - st::ControlGraph controlGraph; + st::ParsedMethod* parsedMethod; + st::ControlGraph* controlGraph; st::InstructionNode* currentNode; - TMethod* method; // Smalltalk method we're currently processing + TMethod* originMethod; // Smalltalk method we're currently processing llvm::Function* function; // LLVM function that is created based on method llvm::IRBuilder<>* builder; // Builder inserts instructions into basic blocks @@ -253,16 +251,21 @@ class MethodCompiler { void pushValue(TStackValue* value); - TJITContext(MethodCompiler* compiler, TMethod* method) - : parsedMethod(method), controlGraph(&parsedMethod), currentNode(0), - method(method), function(0), builder(0), + TJITContext(MethodCompiler* compiler, TMethod* method, bool parse = true) + : currentNode(0), originMethod(method), function(0), builder(0), preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), contextHolder(0), selfHolder(0) { - controlGraph.buildGraph(); + if (parse) { + parsedMethod = new st::ParsedMethod(method); + controlGraph = new st::ControlGraph(parsedMethod); + controlGraph->buildGraph(); + } }; ~TJITContext() { + delete controlGraph; + if (! basicBlockContexts.empty()) { TBlockContextMap::iterator iContext = basicBlockContexts.begin(); while (iContext != basicBlockContexts.end()) { @@ -284,6 +287,28 @@ class MethodCompiler { } }; + struct TJITBlockContext : public TJITContext { + st::ParsedBlock* parsedBlock; + + TJITBlockContext( + MethodCompiler* compiler, + st::ParsedMethod* method, + st::ParsedBlock* block + ) + : TJITContext(compiler, 0, false), parsedBlock(block) + { + parsedMethod = method; + originMethod = parsedMethod->getOrigin(); + controlGraph = new st::ControlGraph(method, block); + controlGraph->buildGraph(); + } + + ~TJITBlockContext() { + parsedMethod = 0; // We do not want TJITContext to delete this + } + }; + + private: llvm::Module* m_JITModule; std::map m_targetToBlockMap; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 5b7d7a1..24b70fe 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -90,7 +90,7 @@ Value* TDeferredValue::get() } break; case loadLiteral: { - TMethod* method = m_jit->method; + TMethod* method = m_jit->originMethod; TObject* literal = method->literals->getField(m_index); Value* literalValue = builder.CreateIntToPtr( @@ -371,7 +371,7 @@ bool MethodCompiler::scanForBlockReturn(TJITContext& jit, uint32_t byteCount/* = // the method's code to ensure that try-catch is really needed. If it is not, we simply // skip its generation. - st::BlockReturnDetector detector(&jit.parsedMethod); + st::BlockReturnDetector detector(jit.parsedMethod); detector.run(); return detector.isBlockReturnFound(); @@ -384,7 +384,7 @@ void MethodCompiler::scanForBranches(TJITContext& jit, uint32_t byteCount /*= 0* class Visitor : public st::BasicBlockVisitor { public: - Visitor(TJITContext& jit) : BasicBlockVisitor(&jit.parsedMethod), m_jit(jit) { } + Visitor(TJITContext& jit) : BasicBlockVisitor(jit.parsedMethod), m_jit(jit) { } private: virtual bool visitBlock(st::BasicBlock& basicBlock) { @@ -487,7 +487,7 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) { class Visitor : public st::NodeVisitor { public: - Visitor(TJITContext& jit) : st::NodeVisitor(&jit.controlGraph), m_jit(jit) { } + Visitor(TJITContext& jit) : st::NodeVisitor(jit.controlGraph), m_jit(jit) { } private: virtual bool visitDomain(st::ControlDomain& domain) { @@ -500,9 +500,10 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) } virtual bool visitNode(st::ControlNode& node) { - m_jit.currentNode = (&node)->cast(); - m_jit.compiler->writeInstruction(m_jit); + m_jit.currentNode = node.cast(); + assert(m_jit.currentNode); + m_jit.compiler->writeInstruction(m_jit); return NodeVisitor::visitNode(node); } @@ -544,7 +545,7 @@ void MethodCompiler::writeInstruction(TJITContext& jit) { default: std::fprintf(stderr, "JIT: Invalid opcode %d at node %d in method %s\n", jit.currentNode->getInstruction().getOpcode(), - jit.currentNode->getIndex(), jit.method->name->toString().c_str()); + jit.currentNode->getIndex(), jit.originMethod->name->toString().c_str()); } } @@ -644,12 +645,15 @@ void MethodCompiler::doPushConstant(TJITContext& jit) void MethodCompiler::doPushBlock(TJITContext& jit) { - TJITContext blockContext(this, jit.method); // FIXME + st::PushBlockNode* pushBlockNode = jit.currentNode->cast(); + st::ParsedBlock* parsedBlock = pushBlockNode->getParsedBlock(); + + TJITBlockContext blockContext(this, jit.parsedMethod, parsedBlock); // Creating block function named Class>>method@offset - const uint16_t nodeIndex = jit.currentNode->getIndex(); + const uint16_t blockOffset = parsedBlock->getStartOffset(); std::ostringstream ss; - ss << jit.method->klass->name->toString() + ">>" + jit.method->name->toString() << "@" << nodeIndex; + ss << jit.originMethod->klass->name->toString() + ">>" + jit.originMethod->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); std::vector blockParams; @@ -688,7 +692,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) Value* args[] = { jit.getCurrentContext(), // creatingContext jit.builder->getInt8(jit.currentNode->getInstruction().getArgument()), // arg offset - jit.builder->getInt16(nodeIndex) + jit.builder->getInt16(blockOffset) }; Value* blockObject = jit.builder->CreateCall(m_runtimeAPI.createBlock, args); blockObject = jit.builder->CreateBitCast(blockObject, m_baseTypes.object->getPointerTo()); @@ -887,7 +891,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) Value* messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); std::ostringstream ss; - ss << "#" << jit.method->literals->getField(jit.currentNode->getInstruction().getArgument())->toString() << "."; + ss << "#" << jit.originMethod->literals->getField(jit.currentNode->getInstruction().getArgument())->toString() << "."; messageSelector->setName(ss.str()); // Forming a message parameters From a13796e0c2b7c2d865ee60352a8d8395e0edf9a5 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 25 May 2014 15:34:05 +0700 Subject: [PATCH 072/290] Fixes graph constructor Issue: #32 --- src/ControlGraph.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index e504e1f..9c1dfae 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -100,6 +100,9 @@ void GraphConstructor::processNode(InstructionNode* node) case opcode::pushArgument: case opcode::pushTemporary: // TODO Link with tau node case opcode::pushInstance: + m_currentDomain->pushValue(node); + break; + case opcode::pushBlock: { const uint16_t blockEndOffset = node->getInstruction().getExtra(); ParsedMethod* const parsedMethod = m_graph->getParsedMethod(); From 88cc5837b0506ab09765052413b62f19bc1d3842 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 1 Jun 2014 20:39:13 +0700 Subject: [PATCH 073/290] Adds methods for binding graph nodes and llvm values Each node of the control graph may be associated with llvm::Value object. Method compiler iterates through the control graph and creates values which then bound to graph nodes. Some graph nodes refer other nodes as their arguments. This relation is reflected by similar Value relations. When Value creation require argument which had not been processed yet, dummy value is inserted. This dummy value will be replaced later when graph node holding it will be processed. Issue: #32 --- include/analysis.h | 15 +++- include/jit.h | 5 ++ src/MethodCompiler.cpp | 173 +++++++++++++++++++++++++++++------------ 3 files changed, 141 insertions(+), 52 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index d1ff230..bcf74c1 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -2,9 +2,12 @@ #define LLST_ANALYSIS_INCLUDED #include - #include +namespace llvm { + class Value; +} + namespace st { // This detector scans method's nested blocks for block return instruction. @@ -91,7 +94,7 @@ class ControlNode { ntTau // virtual node linkinig variable types from assignment sites }; - ControlNode(uint32_t index) : m_index(index), m_domain(0) { } + ControlNode(uint32_t index) : m_index(index), m_domain(0), m_value(0) { } virtual ~ControlNode() { } virtual TNodeType getNodeType() const = 0; @@ -116,13 +119,17 @@ class ControlNode { m_outEdges.erase(to); to->getInEdges().erase(this); } + + void setValue(llvm::Value* value) { m_value = value; } + llvm::Value* getValue() const { return m_value; } private: uint32_t m_index; TNodeType m_type; TNodeSet m_inEdges; TNodeSet m_outEdges; - ControlDomain* m_domain; + + llvm::Value* m_value; }; // Instruction node represents a signle VM instruction and it's relations in code. @@ -134,7 +141,7 @@ class InstructionNode : public ControlNode { void setInstruction(TSmalltalkInstruction instruction) { m_instruction = instruction; } const TSmalltalkInstruction& getInstruction() const { return m_instruction; } - ControlNode* getArgument(const std::size_t index) const { + ControlNode* getArgument(const std::size_t index = 0) const { assert(index >= 0 && index < m_arguments.size()); return m_arguments[index]; } diff --git a/include/jit.h b/include/jit.h index 3bb2228..f637a4b 100644 --- a/include/jit.h +++ b/include/jit.h @@ -323,6 +323,11 @@ class MethodCompiler { TExceptionAPI m_exceptionAPI; TBaseFunctions m_baseFunctions; + llvm::Value* processLeafNode(st::InstructionNode* instruction); + llvm::Value* getNodeValue(TJITContext& jit, st::ControlNode* node); + void setNodeValue(st::ControlNode* node, llvm::Value* value); + llvm::Value* getArgument(TJITContext& jit, std::size_t index = 0); + llvm::Value* allocateRoot(TJITContext& jit, llvm::Type* type); llvm::Value* protectPointer(TJITContext& jit, llvm::Value* value); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 24b70fe..cd6b632 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -705,7 +705,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) void MethodCompiler::doAssignTemporary(TJITContext& jit) { uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* value = jit.lastValue(); + Value* value = jit.currentNode->getArgument()->getValue(); //jit.lastValue(); IRBuilder<>& builder = * jit.builder; Function* getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); @@ -717,7 +717,7 @@ void MethodCompiler::doAssignTemporary(TJITContext& jit) void MethodCompiler::doAssignInstance(TJITContext& jit) { uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* value = jit.lastValue(); + Value* value = jit.currentNode->getArgument()->getValue(); // jit.lastValue(); IRBuilder<>& builder = * jit.builder; Value* self = jit.getSelf(); @@ -739,8 +739,9 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) // Filling object with contents uint8_t index = argumentsCount; + assert(argumentsCount == jit.currentNode->getArgumentsCount()); while (index > 0) { - Value* value = jit.popValue(); + Value* value = getArgument(jit, index - 1); // jit.popValue(); jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), value); } @@ -752,7 +753,7 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) void MethodCompiler::doSendUnary(TJITContext& jit) { - Value* value = jit.popValue(); + Value* value = getArgument(jit); // jit.popValue(); Value* condition = 0; switch ( static_cast(jit.currentNode->getInstruction().getArgument()) ) { @@ -764,7 +765,75 @@ void MethodCompiler::doSendUnary(TJITContext& jit) } Value* result = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); - jit.pushValue(result); + setNodeValue(jit.currentNode, result); + //jit.pushValue(result); +} + +llvm::Value* MethodCompiler::processLeafNode(st::InstructionNode* instruction) +{ + +} + +llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = 0*/) { + return getNodeValue(jit, jit.currentNode->getArgument(index)); +} + +Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node) +{ + Value* value = node->getValue(); + if (value) + return value; + + const std::size_t inEdgesCount = node->getInEdges().size(); + if (st::InstructionNode* instruction = node->cast()) { + // If node is a leaf from the same domain we may encode it locally. + // If not, creating a dummy value wich will be replaced by real value later. + + if (!inEdgesCount && instruction->getDomain() == jit.currentNode->getDomain()) + value = processLeafNode(instruction); + else + value = UndefValue::get(jit.builder->getVoidTy()); + + } else if (st::PhiNode* phiNode = node->cast()) { + BasicBlock* const insertBlock = jit.builder->GetInsertBlock(); + const BasicBlock::iterator storedInsertionPoint = jit.builder->GetInsertPoint(); + const BasicBlock::iterator firstInsertionPoint = insertBlock->getFirstInsertionPt(); + jit.builder->SetInsertPoint(insertBlock, firstInsertionPoint); + + PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object, inEdgesCount); + + st::TNodeSet::iterator iNode = phiNode->getInEdges().begin(); + for (; iNode != phiNode->getInEdges().end(); ++iNode) { + st::ControlNode* const inNode = *iNode; + BasicBlock* const inBlock = m_targetToBlockMap[inNode->getDomain()->getBasicBlock()->getOffset()]; + assert(inBlock); + + phiValue->addIncoming(getNodeValue(jit, inNode), inBlock); + } + + phiNode->setValue(phiValue); + jit.builder->SetInsertPoint(insertBlock, storedInsertionPoint); + + value = phiValue; + } + + assert(value); + return value; +} + +void MethodCompiler::setNodeValue(st::ControlNode* node, llvm::Value* value) +{ + assert(value); + + Value* oldValue = node->getValue(); + if (oldValue) { + if (isa(oldValue)) + oldValue->replaceAllUsesWith(value); + else + assert(false); + } + + node->setValue(value); } void MethodCompiler::doSendBinary(TJITContext& jit) @@ -772,8 +841,8 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // 0, 1 or 2 for '<', '<=' or '+' respectively binaryBuiltIns::Operator opcode = static_cast(jit.currentNode->getInstruction().getArgument()); - Value* rightValue = jit.popValue(); - Value* leftValue = jit.popValue(); + Value* rightValue = getArgument(jit, 1); // jit.popValue(); + Value* leftValue = getArgument(jit, 0); // jit.popValue(); // Checking if values are both small integers Value* rightIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, rightValue); @@ -796,7 +865,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) Value* rightInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, rightValue); Value* leftInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, leftValue); - Value* intResult = 0; // this will be an immediate operation result + Value* intResult = 0; // this will be an immediate operation result Value* intResultObject = 0; // this will be actual object to return switch (opcode) { case binaryBuiltIns::operatorLess : intResult = jit.builder->CreateICmpSLT(leftInt, rightInt); break; @@ -879,12 +948,13 @@ void MethodCompiler::doSendBinary(TJITContext& jit) phi->addIncoming(sendMessageResult, sendBinaryBlock); Value* resultHolder = protectPointer(jit, phi); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); + setNodeValue(jit.currentNode, resultHolder); + //jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } void MethodCompiler::doSendMessage(TJITContext& jit) { - Value* arguments = jit.popValue(); + Value* arguments = getArgument(jit); // jit.popValue(); // First of all we need to get the actual message selector Value* selectorObject = jit.getLiteral(jit.currentNode->getInstruction().getArgument()); @@ -944,13 +1014,13 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::stackReturn: if ( !iPreviousInst->isTerminator() && jit.hasValue() ) - jit.builder->CreateRet(jit.popValue()); + jit.builder->CreateRet(getArgument(jit)); // jit.popValue()); break; case special::blockReturn: if ( !iPreviousInst->isTerminator() && jit.hasValue()) { // Peeking the return value from the stack - Value* value = jit.popValue(); + Value* value = getArgument(jit); // jit.popValue(); // Loading the target context information Value* blockContext = jit.builder->CreateBitCast(jit.getCurrentContext(), m_baseTypes.block->getPointerTo()); @@ -971,21 +1041,25 @@ void MethodCompiler::doSpecial(TJITContext& jit) // We're popping the value from the stack to a temporary holder // and then pushing two lazy stack values pointing to it. - Value* dupValue = jit.popValue(); + Value* dupValue = getArgument(jit); // jit.popValue(); Value* dupHolder = protectPointer(jit, dupValue); dupHolder->setName("pDup."); // Two equal values are pushed on the stack - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, dupHolder)); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, dupHolder)); + jit.currentNode->setValue(dupHolder); + + // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, dupHolder)); + // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, dupHolder)); } //jit.pushValue(jit.lastValue()); break; case special::popTop: - if (jit.hasValue()) - jit.popValue(0, true); + // This should be completely eliminated by graph constructor + assert(false); +// if (jit.hasValue()) +// jit.popValue(0, true); break; case special::branch: { @@ -1019,7 +1093,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) // Creating condition check Value* boolObject = (opcode == special::branchIfTrue) ? m_globals.trueObject : m_globals.falseObject; - Value* condition = jit.popValue(); + Value* condition = getArgument(jit); // jit.popValue(); Value* boolValue = jit.builder->CreateICmpEQ(condition, boolObject); jit.builder->CreateCondBr(boolValue, targetBlock, skipBlock); @@ -1033,7 +1107,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) } break; case special::sendToSuper: { - Value* argsObject = jit.popValue(); + Value* argsObject = getArgument(jit); // jit.popValue(); Value* arguments = jit.builder->CreateBitCast(argsObject, m_baseTypes.objectArray->getPointerTo()); uint32_t literalIndex = jit.currentNode->getInstruction().getExtra(); @@ -1088,6 +1162,7 @@ void MethodCompiler::doPrimitive(TJITContext& jit) jit.basicBlockContexts[primitiveFailedBB].referers.insert(jit.builder->GetInsertBlock()); compilePrimitive(jit, opcode, primitiveResult, primitiveFailed, primitiveSucceededBB, primitiveFailedBB); + jit.currentNode->setValue(primitiveResult); // Linking pop chain jit.basicBlockContexts[primitiveSucceededBB].referers.insert(jit.builder->GetInsertBlock()); @@ -1111,8 +1186,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, { switch (opcode) { case primitive::objectsAreEqual: { - Value* object2 = jit.popValue(); - Value* object1 = jit.popValue(); + Value* object2 = getArgument(jit, 1); // jit.popValue(); + Value* object1 = getArgument(jit, 0); // jit.popValue(); Value* result = jit.builder->CreateICmpEQ(object1, object2); Value* boolValue = jit.builder->CreateSelect(result, m_globals.trueObject, m_globals.falseObject); @@ -1122,7 +1197,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, // TODO ioGetchar case primitive::ioPutChar: { - Value* intObject = jit.popValue(); + Value* intObject = getArgument(jit); // jit.popValue(); Value* intValue = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, intObject); Value* charValue = jit.builder->CreateTrunc(intValue, jit.builder->getInt8Ty()); @@ -1133,12 +1208,12 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, } break; case primitive::getClass: { - Value* object = jit.popValue(); + Value* object = getArgument(jit); // jit.popValue(); Value* klass = jit.builder->CreateCall(m_baseFunctions.getObjectClass, object, "class"); primitiveResult = jit.builder->CreateBitCast(klass, m_baseTypes.object->getPointerTo()); } break; case primitive::getSize: { - Value* object = jit.popValue(); + Value* object = getArgument(jit); // jit.popValue(); Value* objectIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, object, "isSmallInt"); BasicBlock* asSmallInt = BasicBlock::Create(m_JITModule->getContext(), "asSmallInt", jit.function); @@ -1155,8 +1230,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, } break; case primitive::startNewProcess: { // 6 - /* ticks. unused */ jit.popValue(); - Value* processObject = jit.popValue(); + // /* ticks. unused */ jit.popValue(); + Value* processObject = getArgument(jit, 1); // jit.popValue(); Value* process = jit.builder->CreateBitCast(processObject, m_baseTypes.process->getPointerTo()); Function* executeProcess = m_JITModule->getFunction("executeProcess"); @@ -1166,8 +1241,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, } break; case primitive::allocateObject: { // 7 - Value* sizeObject = jit.popValue(); - Value* klassObject = jit.popValue(); + Value* sizeObject = getArgument(jit, 1); // jit.popValue(); + Value* klassObject = getArgument(jit, 0); // jit.popValue(); Value* klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); Value* size = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, sizeObject, "size."); @@ -1178,8 +1253,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, } break; case primitive::allocateByteArray: { // 20 - Value* sizeObject = jit.popValue(); - Value* klassObject = jit.popValue(); + Value* sizeObject = getArgument(jit, 1); // jit.popValue(); + Value* klassObject = getArgument(jit, 0); // jit.popValue(); Value* klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); Value* dataSize = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, sizeObject, "dataSize."); @@ -1189,8 +1264,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, } break; case primitive::cloneByteObject: { // 23 - Value* klassObject = jit.popValue(); - Value* original = jit.popValue(); + Value* klassObject = getArgument(jit, 1); // jit.popValue(); + Value* original = getArgument(jit, 0); // jit.popValue(); Value* originalHolder = protectPointer(jit, original); Value* klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); @@ -1223,11 +1298,11 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, } break; case primitive::integerNew: - primitiveResult = jit.popValue(); // TODO long integers + primitiveResult = getArgument(jit); // jit.popValue(); // TODO long integers break; case primitive::blockInvoke: { // 8 - Value* object = jit.popValue(); + Value* object = getArgument(jit); // jit.popValue(); Value* block = jit.builder->CreateBitCast(object, m_baseTypes.block->getPointerTo()); int32_t argCount = jit.currentNode->getInstruction().getArgument() - 1; @@ -1258,7 +1333,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, { // (*blockTemps)[argumentLocation + index] = stack[--ec.stackTop]; Value* fieldIndex = jit.builder->CreateAdd(argumentLocation, jit.builder->getInt32(index)); - Value* argument = jit.popValue(); + Value* argument = getArgument(jit, index); // jit.popValue(); jit.builder->CreateCall3(m_baseFunctions.setObjectField, blockTemps, fieldIndex, argument); } @@ -1290,9 +1365,10 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, case primitive::arrayAt: // 24 case primitive::arrayAtPut: { // 5 - Value* indexObject = jit.popValue(); - Value* arrayObject = jit.popValue(); - Value* valueObejct = (opcode == primitive::arrayAtPut) ? jit.popValue() : 0; + std::size_t argIndex = (opcode == primitive::arrayAtPut) ? 2 : 1; + Value* indexObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* arrayObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* valueObejct = (opcode == primitive::arrayAtPut) ? getArgument(jit, argIndex--) : 0; BasicBlock* indexChecked = BasicBlock::Create(m_JITModule->getContext(), "indexChecked.", jit.function); @@ -1326,9 +1402,10 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, case primitive::stringAt: // 21 case primitive::stringAtPut: { // 22 - Value* indexObject = jit.popValue(); - Value* stringObject = jit.popValue(); - Value* valueObejct = (opcode == primitive::stringAtPut) ? jit.popValue() : 0; + std::size_t argIndex = (opcode == primitive::stringAtPut) ? 2 : 1; + Value* indexObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* stringObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* valueObejct = (opcode == primitive::stringAtPut) ? getArgument(jit, argIndex--) : 0; BasicBlock* indexChecked = BasicBlock::Create(m_JITModule->getContext(), "indexChecked.", jit.function); @@ -1384,17 +1461,17 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, case primitive::smallIntBitOr: // 36 case primitive::smallIntBitAnd: // 37 case primitive::smallIntBitShift: { // 39 - Value* rightObject = jit.popValue(); - Value* leftObject = jit.popValue(); + Value* rightObject = getArgument(jit, 1); // jit.popValue(); + Value* leftObject = getArgument(jit, 0); // jit.popValue(); compileSmallIntPrimitive(jit, opcode, leftObject, rightObject, primitiveResult, primitiveFailedBB); } break; case primitive::bulkReplace: { - Value* destination = jit.popValue(); - Value* sourceStartOffset = jit.popValue(); - Value* source = jit.popValue(); - Value* destinationStopOffset = jit.popValue(); - Value* destinationStartOffset = jit.popValue(); + Value* destination = getArgument(jit, 4); // jit.popValue(); + Value* sourceStartOffset = getArgument(jit, 3); // jit.popValue(); + Value* source = getArgument(jit, 2); // jit.popValue(); + Value* destinationStopOffset = getArgument(jit, 1); // jit.popValue(); + Value* destinationStartOffset = getArgument(jit, 0); // jit.popValue(); Value* arguments[] = { destination, From d615752c618d1ab80f9169a2c60b0e6e5f217b41 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 13 Jun 2014 13:48:59 +0700 Subject: [PATCH 074/290] Adds ControlNode::getConsumers() This function return a list of nodes that refer current node as one of their arguments. Such information is requested rarely and almost once per node, so it's better to calculate the list rather store it in node. Issue: #32 --- include/analysis.h | 3 +++ src/ControlGraph.cpp | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/analysis.h b/include/analysis.h index bcf74c1..d24a46e 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -122,6 +122,9 @@ class ControlNode { void setValue(llvm::Value* value) { m_value = value; } llvm::Value* getValue() const { return m_value; } + + // Returns a list of nodes which refer current node as argument + TNodeList getConsumers(); private: uint32_t m_index; TNodeType m_type; diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 9c1dfae..ad7ed4c 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -12,6 +12,23 @@ bool DomainOffsetCompare::operator() (const ControlDomain* a, const ControlDomai return a->getBasicBlock()->getOffset() < b->getBasicBlock()->getOffset(); } +TNodeList ControlNode::getConsumers() { + TNodeList consumers; + + TNodeSet::iterator iNode = m_outEdges.begin(); + for (; iNode != m_outEdges.end(); ++iNode) { + if (InstructionNode* const instruction = (*iNode)->cast()) { + for (std::size_t index = 0; index < instruction->getArgumentsCount(); index++) + if (instruction->getArgument(index) == this) + consumers.push_back(instruction); + } else if (PhiNode* const phi = (*iNode)->cast()) { + consumers.push_back(phi); + } + } + + return consumers; +} + template<> InstructionNode* ControlNode::cast() { return this->getNodeType() == ntInstruction ? static_cast(this) : 0; } template<> PhiNode* ControlNode::cast() { return this->getNodeType() == ntPhi ? static_cast(this) : 0; } template<> TauNode* ControlNode::cast() { return this->getNodeType() == ntTau ? static_cast(this) : 0; } From 6a17c801389ce3c8ed12c54bffb96fd42ed3cd2e Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 13 Jun 2014 17:14:49 +0700 Subject: [PATCH 075/290] Fixes graph linker (related to ABAB problem) Previous implementation incorrectly processed block chains with non-empty stacks. Consider the following situation: A(-2) <- B(1) <- C(1) Block A is the value consumer which requests two values from the refering blocks. Each of blocks B and C provide a single value on their local stack. First request with index 0 will be satisfied by block B because it has 1 value on the local stack. Request with index 1 is more complex. First, block A looks into block B because it is the only referer. But block B have only 1 value on the stack and therefore could not provide a value with index 1. Hence, block C is queried recursively. The problem is that block C also have only 1 value and could not satisfy request with index 1. Problem is solved by substracting the size of local stack from the requested index on each recursion entry. Finally, second value will be requested from block C with effective index 1 - 1 = 0. Issue: #32 Issue: #2 --- src/ControlGraph.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index ad7ed4c..9ba501b 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -383,7 +383,9 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar if (!refererStackSize || argumentIndex > refererStackSize - 1) { // Referer block do not have enough values on it's stack. // We need to go deeper and process it's referers in turn. - ControlNode* const refererValue = getRequestedNode(refererDomain, argumentIndex); + const std::size_t newIndex = argumentIndex - refererStackSize; + assert(newIndex <= argumentIndex); + ControlNode* const refererValue = getRequestedNode(refererDomain, newIndex); if (singleReferer) result = refererValue; @@ -392,6 +394,7 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar } else { const std::size_t valueIndex = refererStackSize - 1 - argumentIndex; + assert(valueIndex < refererStackSize); ControlNode* const stackValue = refererStack[valueIndex]; if (singleReferer) From ad5b9c41871ba88d65de690f407a6715c168c0e3 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 16 Jun 2014 15:48:39 +0700 Subject: [PATCH 076/290] Refactors ControlNode::getConsumers() Function now returns boolean value which is true when at least one consumer was found. Consumer list is returned via out parameter. Issue: #32 --- include/analysis.h | 5 +++-- src/ControlGraph.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index d24a46e..77e8dd7 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -123,8 +123,9 @@ class ControlNode { void setValue(llvm::Value* value) { m_value = value; } llvm::Value* getValue() const { return m_value; } - // Returns a list of nodes which refer current node as argument - TNodeList getConsumers(); + // Get a list of nodes which refer current node as argument + // Return true if at least one consumer is found + bool getConsumers(TNodeList& result); private: uint32_t m_index; TNodeType m_type; diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 9ba501b..dcd0022 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -12,21 +12,21 @@ bool DomainOffsetCompare::operator() (const ControlDomain* a, const ControlDomai return a->getBasicBlock()->getOffset() < b->getBasicBlock()->getOffset(); } -TNodeList ControlNode::getConsumers() { - TNodeList consumers; +bool ControlNode::getConsumers(TNodeList& result) { + result.clear(); TNodeSet::iterator iNode = m_outEdges.begin(); for (; iNode != m_outEdges.end(); ++iNode) { if (InstructionNode* const instruction = (*iNode)->cast()) { for (std::size_t index = 0; index < instruction->getArgumentsCount(); index++) if (instruction->getArgument(index) == this) - consumers.push_back(instruction); + result.push_back(instruction); } else if (PhiNode* const phi = (*iNode)->cast()) { - consumers.push_back(phi); + result.push_back(phi); } } - return consumers; + return !result.empty(); } template<> InstructionNode* ControlNode::cast() { return this->getNodeType() == ntInstruction ? static_cast(this) : 0; } From c35916ad0d20dd1c76154a66d071ebf6555b4bc6 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 16 Jun 2014 15:49:23 +0700 Subject: [PATCH 077/290] Refactors graph optimizer by using ControlNode::getConsumers() Issue: #32 --- src/ControlGraph.cpp | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index dcd0022..8aa588f 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -427,8 +427,8 @@ class GraphOptimizer : public NodeVisitor { if (!nodeInstruction.isTrivial() || !nodeInstruction.isValueProvider()) return NodeVisitor::visitNode(node); - TNodeSet consumers; - if (! getConsumers(instruction, consumers)) { + TNodeList consumers; + if (! instruction->getConsumers(consumers)) { std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); m_nodesToRemove.push_back(instruction); } else if (consumers.size() == 1) { @@ -455,24 +455,6 @@ class GraphOptimizer : public NodeVisitor { removeNode(*iNode); } private: - bool getConsumers(InstructionNode* node, TNodeSet& consumers) { - consumers.clear(); - - const TNodeSet& outEdges = node->getOutEdges(); - TNodeSet::iterator iEdge = outEdges.begin(); - for (; iEdge != outEdges.end(); ++iEdge) { - if (InstructionNode* const instruction = (*iEdge)->cast()) { - const std::size_t argsCount = instruction->getArgumentsCount(); - for (std::size_t index = 0; index < argsCount; index++) { - if (instruction->getArgument(index) == node) - consumers.insert(instruction); - } - } - } - - return !consumers.empty(); - } - void removeNode(ControlNode* node) { // TODO } From d44b00a45682d78a2a13a3463327a5c9d55eaa22 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 13 Jun 2014 18:04:18 +0700 Subject: [PATCH 078/290] Removes value stack methods, replaces popValue() with getArument() Issue: #32 --- include/jit.h | 6 -- src/MethodCompiler.cpp | 163 +++++------------------------------------ 2 files changed, 18 insertions(+), 151 deletions(-) diff --git a/include/jit.h b/include/jit.h index f637a4b..86f4445 100644 --- a/include/jit.h +++ b/include/jit.h @@ -236,10 +236,6 @@ class MethodCompiler { TBlockContextMap basicBlockContexts; MethodCompiler* compiler; // link to outer class for variable access - bool hasValue(); - void pushValue(llvm::Value* value); - llvm::Value* lastValue(); - llvm::Value* popValue(llvm::BasicBlock* overrideBlock = 0, bool dropValue = false); llvm::Value* contextHolder; llvm::Value* selfHolder; @@ -249,8 +245,6 @@ class MethodCompiler { llvm::Value* getMethodClass(); llvm::Value* getLiteral(uint32_t index); - void pushValue(TStackValue* value); - TJITContext(MethodCompiler* compiler, TMethod* method, bool parse = true) : currentNode(0), originMethod(method), function(0), builder(0), preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index cd6b632..33d13d8 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -139,136 +139,6 @@ Value* MethodCompiler::TJITContext::getMethodClass() return klass; } -void MethodCompiler::TJITContext::pushValue(TStackValue* value) -{ - // Values are always pushed to the local stack - basicBlockContexts[builder->GetInsertBlock()].valueStack.push_back(value); -} - -void MethodCompiler::TJITContext::pushValue(Value* value) -{ - // Values are always pushed to the local stack - basicBlockContexts[builder->GetInsertBlock()].valueStack.push_back(new TPlainValue(value)); -} - -Value* MethodCompiler::TJITContext::lastValue() -{ - TValueStack& valueStack = basicBlockContexts[builder->GetInsertBlock()].valueStack; - - // Popping value from the referer's block - // and creating phi function if necessary - Value* value = popValue(); - - // Pushing the value locally (may be phi) - valueStack.push_back(new TPlainValue(value)); - - // Returning it as a last value - return value; -} - -bool MethodCompiler::TJITContext::hasValue() -{ - TBasicBlockContext& blockContext = basicBlockContexts[builder->GetInsertBlock()]; - - // If local stack is not empty, then we definitly have some value - if (! blockContext.valueStack.empty()) - return true; - - // If not, checking the possible referers - if (blockContext.referers.empty()) - return false; // no referers == no value - - // FIXME This is not correct in a case of dummy transitive block with an only simple branch - // Every referer should have equal number of values on the stack - // so we may check any referer's stack to see if it has value - return ! basicBlockContexts[*blockContext.referers.begin()].valueStack.empty(); -} - -Value* MethodCompiler::TJITContext::popValue(BasicBlock* overrideBlock /* = 0*/, bool dropValue /*= false*/) -{ - TBasicBlockContext& blockContext = basicBlockContexts[overrideBlock ? overrideBlock : builder->GetInsertBlock()]; - TValueStack& valueStack = blockContext.valueStack; - - if (! valueStack.empty()) { - // If local stack is not empty - // then we simply pop the value from it - TStackValue* stackValue = valueStack.back(); - Value* result = 0; - - if (!dropValue) { - result = stackValue->get(); // NOTE May and probably will perform code injection - } - - delete stackValue; - valueStack.pop_back(); - - return result; - } else { - // If value stack is empty then it means that we're dealing with - // a value pushed in the predcessor block (or a stack underflow) - - // If there is a single predcessor, then we simply pop that value - // If there are several predcessors we need to create a phi function - switch (blockContext.referers.size()) { - case 0: - /* TODO no referers, empty local stack and pop operation = error */ - outs() << "Value stack underflow\n"; - std::exit(1); - return compiler->m_globals.nilObject; - - case 1: { - // Recursively processing referer's block - BasicBlock* referer = *blockContext.referers.begin(); - Value* value = popValue(referer, dropValue); - return value; - } break; - - default: { - if (dropValue) { - TRefererSet::iterator iReferer = blockContext.referers.begin(); - for (; iReferer != blockContext.referers.end(); ++iReferer) - popValue(*iReferer, true); - return 0; - } - - // Storing current insert position for further use - BasicBlock* currentBasicBlock = builder->GetInsertBlock(); - BasicBlock::iterator currentInsertPoint = builder->GetInsertPoint(); - - BasicBlock* insertBlock = overrideBlock ? overrideBlock : currentBasicBlock; - BasicBlock::iterator firstInsertionPoint = insertBlock->getFirstInsertionPt(); - - if (overrideBlock) { - builder->SetInsertPoint(overrideBlock, firstInsertionPoint); - } else { - if (firstInsertionPoint != insertBlock->end()) - builder->SetInsertPoint(currentBasicBlock, firstInsertionPoint); - } - - // Creating a phi function at the beginning of the block - const uint32_t numReferers = blockContext.referers.size(); - PHINode* phi = builder->CreatePHI(compiler->m_baseTypes.object->getPointerTo(), numReferers, "phi."); - Value* holder = compiler->protectPointer(*this, phi); - - // Filling incoming nodes with values from the referer stacks - TRefererSet::iterator iReferer = blockContext.referers.begin(); - for (; iReferer != blockContext.referers.end(); ++iReferer) { - // FIXME non filled block will not yet have the value - // we need to store them to a special post processing list - // and update the current phi function when value will be available - builder->SetInsertPoint((*iReferer)->getTerminator()); - Value* value = popValue(*iReferer); - phi->addIncoming(value, *iReferer); - } - - builder->SetInsertPoint(currentBasicBlock, currentInsertPoint); - - return builder->CreateLoad(holder); - } - } - } -} - Function* MethodCompiler::createFunction(TMethod* method) { Type* methodParams[] = { m_baseTypes.context->getPointerTo() }; @@ -587,25 +457,28 @@ void MethodCompiler::doPushInstance(TJITContext& jit) // Array elements are instance variables uint8_t index = jit.currentNode->getInstruction().getArgument(); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadInstance, index)); + +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadInstance, index)); } void MethodCompiler::doPushArgument(TJITContext& jit) { uint8_t index = jit.currentNode->getInstruction().getArgument(); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadArgument, index)); + + st::TNodeList consumers = jit.currentNode->getConsumers(); +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadArgument, index)); } void MethodCompiler::doPushTemporary(TJITContext& jit) { uint8_t index = jit.currentNode->getInstruction().getArgument(); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadTemporary, index)); +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadTemporary, index)); } void MethodCompiler::doPushLiteral(TJITContext& jit) { uint8_t index = jit.currentNode->getInstruction().getArgument(); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadLiteral, index)); +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadLiteral, index)); } void MethodCompiler::doPushConstant(TJITContext& jit) @@ -640,7 +513,7 @@ void MethodCompiler::doPushConstant(TJITContext& jit) std::fprintf(stderr, "JIT: unknown push constant %d\n", constant); } - jit.pushValue(constantValue); + jit.currentNode->setValue(constantValue); } void MethodCompiler::doPushBlock(TJITContext& jit) @@ -699,7 +572,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) blockObject->setName("block."); Value* blockHolder = protectPointer(jit, blockObject); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, blockHolder)); +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, blockHolder)); } void MethodCompiler::doAssignTemporary(TJITContext& jit) @@ -748,7 +621,7 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) Value* argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); Value* argsHolder = protectPointer(jit, argumentsArray); argsHolder->setName("pArgs."); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); } void MethodCompiler::doSendUnary(TJITContext& jit) @@ -995,7 +868,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) } Value* resultHolder = protectPointer(jit, result); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } void MethodCompiler::doSpecial(TJITContext& jit) @@ -1013,12 +886,12 @@ void MethodCompiler::doSpecial(TJITContext& jit) break; case special::stackReturn: - if ( !iPreviousInst->isTerminator() && jit.hasValue() ) +// if ( !iPreviousInst->isTerminator() && jit.hasValue() ) jit.builder->CreateRet(getArgument(jit)); // jit.popValue()); break; case special::blockReturn: - if ( !iPreviousInst->isTerminator() && jit.hasValue()) { + /*if ( !iPreviousInst->isTerminator() && jit.hasValue())*/ { // Peeking the return value from the stack Value* value = getArgument(jit); // jit.popValue(); @@ -1129,7 +1002,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) Value* result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); Value* resultHolder = protectPointer(jit, result); - jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); +// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } break; default: @@ -1173,7 +1046,7 @@ void MethodCompiler::doPrimitive(TJITContext& jit) jit.builder->CreateRet(primitiveResult); jit.builder->SetInsertPoint(primitiveFailedBB); - jit.pushValue(m_globals.nilObject); +// jit.pushValue(m_globals.nilObject); } @@ -1487,8 +1360,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, } break; case primitive::LLVMsendMessage: { - Value* args = jit.builder->CreateBitCast( jit.popValue(), m_baseTypes.objectArray->getPointerTo() ); - Value* selector = jit.builder->CreateBitCast( jit.popValue(), m_baseTypes.symbol->getPointerTo() ); + Value* args = jit.builder->CreateBitCast( getArgument(jit, 1) /*jit.popValue()*/, m_baseTypes.objectArray->getPointerTo() ); + Value* selector = jit.builder->CreateBitCast( getArgument(jit, 0) /*jit.popValue()*/, m_baseTypes.symbol->getPointerTo() ); Value* context = jit.getCurrentContext(); Value* sendMessageArgs[] = { @@ -1529,7 +1402,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, // Filling object with contents uint8_t index = argumentsCount; while (index > 0) { - Value* value = jit.popValue(); + Value* value = getArgument(jit); // jit.popValue(); jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), value); } From a2e23a4a4bd41cfc6b11543ab3568a7f4dfd0080 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 18 Jun 2014 14:05:15 +0700 Subject: [PATCH 079/290] Reverts push instructions to simple holder style Issue: #32 --- src/MethodCompiler.cpp | 89 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 33d13d8..0af13b6 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -453,38 +453,88 @@ void MethodCompiler::writeLandingPad(TJITContext& jit) void MethodCompiler::doPushInstance(TJITContext& jit) { + const uint8_t index = jit.currentNode->getInstruction().getArgument(); + Function* const getObjectField = m_JITModule->getFunction("getObjectField"); + // Self is interpreted as object array. - // Array elements are instance variables + // Array elements are instance variables. + Value* const self = jit.getSelf(); + Value* const field = jit.builder->CreateCall2(getObjectField, self, jit.builder->getInt32(index)); - uint8_t index = jit.currentNode->getInstruction().getArgument(); + std::ostringstream ss; + ss << "field" << index << "."; + field->setName(ss.str()); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadInstance, index)); + Value* const holder = protectPointer(jit, field); + setNodeValue(jit.currentNode, holder); } void MethodCompiler::doPushArgument(TJITContext& jit) { - uint8_t index = jit.currentNode->getInstruction().getArgument(); + /* st::TNodeList consumers; + if (!jit.currentNode->getConsumers(consumers)) { + assert(false); + return; + } + + // If we have only one consumer it's better to encode instruction near it. + // It would be consumer's responsibility to encode us before it's site. + if (consumers.size() == 1) + return; */ - st::TNodeList consumers = jit.currentNode->getConsumers(); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadArgument, index)); + const uint8_t index = jit.currentNode->getInstruction().getArgument(); + + Function* const getArgFromContext = m_JITModule->getFunction("getArgFromContext"); + Value* const context = jit.getCurrentContext(); + Value* const argument = jit.builder->CreateCall2(getArgFromContext, context, jit.builder->getInt32(index)); + + std::ostringstream ss; + ss << "arg" << index << "."; + argument->setName(ss.str()); + + Value* const holder = protectPointer(jit, argument); + setNodeValue(jit.currentNode, holder); } void MethodCompiler::doPushTemporary(TJITContext& jit) { - uint8_t index = jit.currentNode->getInstruction().getArgument(); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadTemporary, index)); + const uint8_t index = jit.currentNode->getInstruction().getArgument(); + + Function* const getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); + Function* const getObjectField = m_JITModule->getFunction("getObjectField"); + + Value* const context = jit.getCurrentContext(); + Value* const temps = jit.builder->CreateCall(getTempsFromContext, context); + Value* const temporary = jit.builder->CreateCall2(getObjectField, temps, jit.builder->getInt32(index)); + + std::ostringstream ss; + ss << "temp" << index << "."; + temporary->setName(ss.str()); + + Value* const holder = protectPointer(jit, temporary); + setNodeValue(jit.currentNode, holder); } void MethodCompiler::doPushLiteral(TJITContext& jit) { - uint8_t index = jit.currentNode->getInstruction().getArgument(); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadLiteral, index)); + const uint8_t index = jit.currentNode->getInstruction().getArgument(); + + Function* const getLiteralFromContext = m_JITModule->getFunction("getLiteralFromContext"); + Value* const context = jit.getCurrentContext(); + Value* const literal = jit.builder->CreateCall2(getLiteralFromContext, context, jit.builder->getInt32(index)); + + std::ostringstream ss; + ss << "lit" << (uint32_t) index << "."; + literal->setName(ss.str()); + + Value* const holder = protectPointer(jit, literal); + setNodeValue(jit.currentNode, holder); } void MethodCompiler::doPushConstant(TJITContext& jit) { const uint32_t constant = jit.currentNode->getInstruction().getArgument(); - Value* constantValue = 0; + Value* constantValue = 0; switch (constant) { case 0: @@ -497,8 +547,8 @@ void MethodCompiler::doPushConstant(TJITContext& jit) case 7: case 8: case 9: { - Value* integerValue = jit.builder->getInt32( TInteger(constant).rawValue() ); - constantValue = jit.builder->CreateIntToPtr(integerValue, m_baseTypes.object->getPointerTo()); + Value* const integerValue = jit.builder->getInt32( TInteger(constant).rawValue() ); + constantValue = jit.builder->CreateIntToPtr(integerValue, m_baseTypes.object->getPointerTo()); std::ostringstream ss; ss << "const" << constant << "."; @@ -513,7 +563,7 @@ void MethodCompiler::doPushConstant(TJITContext& jit) std::fprintf(stderr, "JIT: unknown push constant %d\n", constant); } - jit.currentNode->setValue(constantValue); + setNodeValue(jit.currentNode, constantValue); } void MethodCompiler::doPushBlock(TJITContext& jit) @@ -572,6 +622,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) blockObject->setName("block."); Value* blockHolder = protectPointer(jit, blockObject); + setNodeValue(jit.currentNode, blockHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, blockHolder)); } @@ -654,8 +705,14 @@ llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node) { Value* value = node->getValue(); - if (value) - return value; + if (value) { + // If value is a holder, reading stored value. + // Otherwise, returning as is. + if (isa(value)) + return jit.builder->CreateLoad(value); + else + return value; + } const std::size_t inEdgesCount = node->getInEdges().size(); if (st::InstructionNode* instruction = node->cast()) { From 86a05d286a370f9f344a0596b37358369d949273 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 18 Jun 2014 14:08:02 +0700 Subject: [PATCH 080/290] Removes TDeferredValue and TSimpleValue Issue: #32 --- include/jit.h | 53 -------------------------------- src/MethodCompiler.cpp | 69 ------------------------------------------ 2 files changed, 122 deletions(-) diff --git a/include/jit.h b/include/jit.h index 86f4445..a97e127 100644 --- a/include/jit.h +++ b/include/jit.h @@ -188,18 +188,6 @@ struct TStackValue { virtual llvm::Value* get() = 0; }; -// TPlainValue represents simple llvm::Value* holder which does not -// perform any additional actions when get() is called. Only stored value is returned. -struct TPlainValue : public TStackValue { -private: - llvm::Value* m_value; -public: - TPlainValue(llvm::Value* value) : m_value(value) {} - virtual ~TPlainValue() { } - - virtual llvm::Value* get() { return m_value; } -}; - class MethodCompiler { public: // Some useful type aliases @@ -401,47 +389,6 @@ class MethodCompiler { } }; -// TDeferredValue is used in cases when some particular actions should be done -// when get() method is invoked. Typically this is used to pass an llvm Value* -// to the later code ensuring that it will not be broken by a garbage collection. -struct TDeferredValue : public TStackValue { - enum TOperation { - loadInstance, - loadArgument, - loadTemporary, - loadLiteral, - - // result of message sent - // or pushed block - loadHolder - }; - -private: - TOperation m_operation; - uint32_t m_index; - llvm::Value* m_argument; - - MethodCompiler::TJITContext* m_jit; -public: - TDeferredValue(MethodCompiler::TJITContext* jit, TOperation operation, uint32_t index) { - m_argument = 0; - m_operation = operation; - m_index = index; - m_jit = jit; - } - - TDeferredValue(MethodCompiler::TJITContext* jit, TOperation operation, llvm::Value* argument) { - m_operation = operation; - m_argument = argument; - m_index = 0; - m_jit = jit; - } - - virtual ~TDeferredValue() { } - virtual llvm::Value* get(); -}; - - extern "C" { TObject* newOrdinaryObject(TClass* klass, uint32_t slotSize); TByteObject* newBinaryObject(TClass* klass, uint32_t dataSize); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 0af13b6..d3fc5a6 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -43,75 +43,6 @@ using namespace llvm; -Value* TDeferredValue::get() -{ - IRBuilder<>& builder = * m_jit->builder; - Module* jitModule = JITRuntime::Instance()->getModule(); - Function* getObjectField = jitModule->getFunction("getObjectField"); - - switch (m_operation) { - case loadHolder: - return builder.CreateLoad(m_argument); - - case loadArgument: { - Function* getArgFromContext = jitModule->getFunction("getArgFromContext"); - Value* context = m_jit->getCurrentContext(); - Value* argument = builder.CreateCall2(getArgFromContext, context, builder.getInt32(m_index)); - - std::ostringstream ss; - ss << "arg" << m_index << "."; - argument->setName(ss.str()); - - return argument; - } break; - - case loadInstance: { - Value* self = m_jit->getSelf(); - Value* field = builder.CreateCall2(getObjectField, self, builder.getInt32(m_index)); - - std::ostringstream ss; - ss << "field" << m_index << "."; - field->setName(ss.str()); - - return field; - } break; - - case loadTemporary: { - Function* getTempsFromContext = jitModule->getFunction("getTempsFromContext"); - Value* context = m_jit->getCurrentContext(); - Value* temps = builder.CreateCall(getTempsFromContext, context); - Value* temporary = builder.CreateCall2(getObjectField, temps, builder.getInt32(m_index)); - - std::ostringstream ss; - ss << "temp" << m_index << "."; - temporary->setName(ss.str()); - - return temporary; - } break; - - case loadLiteral: { - TMethod* method = m_jit->originMethod; - TObject* literal = method->literals->getField(m_index); - - Value* literalValue = builder.CreateIntToPtr( - builder.getInt32( reinterpret_cast(literal)), - m_jit->compiler->getBaseTypes().object->getPointerTo() - ); - - std::ostringstream ss; - ss << "lit" << (uint32_t) m_index << "."; - literalValue->setName(ss.str()); - - return literalValue; -// return m_jit->getLiteral(m_index); - } break; - - default: - outs() << "Unknown deferred operation: " << m_operation << "\n"; - return 0; - } -} - Value* MethodCompiler::TJITContext::getLiteral(uint32_t index) { Module* jitModule = JITRuntime::Instance()->getModule(); From 24fff92292c5a3e8d2443fc7539c83a54358cca3 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 18 Jun 2014 14:10:41 +0700 Subject: [PATCH 081/290] Removes unused referer and stack values junk Issue: #32 --- include/jit.h | 56 ------------------------------------------ src/MethodCompiler.cpp | 21 ---------------- 2 files changed, 77 deletions(-) diff --git a/include/jit.h b/include/jit.h index a97e127..0bf8046 100644 --- a/include/jit.h +++ b/include/jit.h @@ -168,43 +168,8 @@ struct TBaseFunctions { } }; -// Value stack is used as a FIFO value holder during the compilation process. -// Software VM uses object arrays to hold the values in dynamic. -// Instead we're interpriting the push, pop and assign instructions -// as a commands which values should be linked together. For example, -// two subsequent instructions 'pushTemporary 1' and 'assignInstance 2' -// will be linked together with effect of instanceVariables[2] = temporaries[1] -// -// TStackValue is a base class for stack values. Descendant value may contain either -// raw value or a deferred command to be done when evaluation takes place. -// Use get() method to get the stored value. -// -// NOTE: Depending on the actual implementation, invoking method get() may result in -// a code generation. This should be done in a known context. -// -// See also: TPlainValue and TDeferredValue -struct TStackValue { - virtual ~TStackValue() { } - virtual llvm::Value* get() = 0; -}; - class MethodCompiler { public: - // Some useful type aliases - typedef std::list TValueStack; - typedef std::set TRefererSet; - - // Block context is a logic encapsulation - // of Smalltalk's CFG and value transitions - struct TBasicBlockContext { - // Compile time stack of values that are - // produced as a result of opcode processing - TValueStack valueStack; - - // Blocks that are referencing - // current block by branching to it - TRefererSet referers; - }; struct TJITContext { st::ParsedMethod* parsedMethod; @@ -220,9 +185,6 @@ class MethodCompiler { llvm::BasicBlock* exceptionLandingPad; bool methodHasBlockReturn; - typedef std::map TBlockContextMap; - TBlockContextMap basicBlockContexts; - MethodCompiler* compiler; // link to outer class for variable access llvm::Value* contextHolder; @@ -247,24 +209,6 @@ class MethodCompiler { ~TJITContext() { delete controlGraph; - - if (! basicBlockContexts.empty()) { - TBlockContextMap::iterator iContext = basicBlockContexts.begin(); - while (iContext != basicBlockContexts.end()) { - TValueStack& valueStack = iContext->second.valueStack; - if (! valueStack.empty()) { - TValueStack::iterator iStackValue = valueStack.begin(); - while (iStackValue != valueStack.end()) { - delete *iStackValue; - ++iStackValue; - } - valueStack.clear(); - } - ++iContext; - } - basicBlockContexts.clear(); - } - delete builder; } }; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index d3fc5a6..5b0747e 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -714,9 +714,6 @@ void MethodCompiler::doSendBinary(TJITContext& jit) BasicBlock* sendBinaryBlock = BasicBlock::Create(m_JITModule->getContext(), "asObjects.", jit.function); BasicBlock* resultBlock = BasicBlock::Create(m_JITModule->getContext(), "result.", jit.function); - // Linking pop-chain within the current logical block - jit.basicBlockContexts[resultBlock].referers.insert(jit.builder->GetInsertBlock()); - // Depending on the contents we may either do the integer operations // directly or create a send message call using operand objects jit.builder->CreateCondBr(isSmallInts, integersBlock, sendBinaryBlock); @@ -842,9 +839,6 @@ void MethodCompiler::doSendMessage(TJITContext& jit) // Creating basic block that will be branched to on normal invoke BasicBlock* nextBlock = BasicBlock::Create(m_JITModule->getContext(), "next.", jit.function); - // Linking pop-chain within the current logical block - jit.basicBlockContexts[nextBlock].referers.insert(jit.builder->GetInsertBlock()); - // Performing a function invoke result = jit.builder->CreateInvoke(m_runtimeAPI.sendMessage, nextBlock, jit.exceptionLandingPad, sendMessageArgs); @@ -932,9 +926,6 @@ void MethodCompiler::doSpecial(TJITContext& jit) // from the previously stored basic blocks BasicBlock* target = m_targetToBlockMap[targetOffset]; jit.builder->CreateBr(target); - - // Updating block referers - jit.basicBlockContexts[target].referers.insert(jit.builder->GetInsertBlock()); } } break; @@ -958,10 +949,6 @@ void MethodCompiler::doSpecial(TJITContext& jit) Value* boolValue = jit.builder->CreateICmpEQ(condition, boolObject); jit.builder->CreateCondBr(boolValue, targetBlock, skipBlock); - // Updating referers - jit.basicBlockContexts[targetBlock].referers.insert(jit.builder->GetInsertBlock()); - jit.basicBlockContexts[skipBlock].referers.insert(jit.builder->GetInsertBlock()); - // Switching to a newly created block jit.builder->SetInsertPoint(skipBlock); } @@ -1019,15 +1006,9 @@ void MethodCompiler::doPrimitive(TJITContext& jit) BasicBlock* primitiveSucceededBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveSucceededBB", jit.function); BasicBlock* primitiveFailedBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveFailedBB", jit.function); - // Linking pop chain - jit.basicBlockContexts[primitiveFailedBB].referers.insert(jit.builder->GetInsertBlock()); - compilePrimitive(jit, opcode, primitiveResult, primitiveFailed, primitiveSucceededBB, primitiveFailedBB); jit.currentNode->setValue(primitiveResult); - // Linking pop chain - jit.basicBlockContexts[primitiveSucceededBB].referers.insert(jit.builder->GetInsertBlock()); - jit.builder->CreateCondBr(primitiveFailed, primitiveFailedBB, primitiveSucceededBB); jit.builder->SetInsertPoint(primitiveSucceededBB); @@ -1185,8 +1166,6 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, Value* blockAcceptsArgCount = jit.builder->CreateSub(tempsSize, argumentLocation); Value* tempSizeOk = jit.builder->CreateICmpSLE(jit.builder->getInt32(argCount), blockAcceptsArgCount); jit.builder->CreateCondBr(tempSizeOk, tempsChecked, primitiveFailedBB); - - jit.basicBlockContexts[tempsChecked].referers.insert(jit.builder->GetInsertBlock()); jit.builder->SetInsertPoint(tempsChecked); // Storing values in the block's wrapping context From 910fe11bbeeba8b539210c339301092045b9980c Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 18 Jun 2014 14:14:15 +0700 Subject: [PATCH 082/290] Adds missing node values Issue: #32 --- src/MethodCompiler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 5b0747e..f3b80a0 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -850,6 +850,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) } Value* resultHolder = protectPointer(jit, result); + setNodeValue(jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } @@ -977,6 +978,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) Value* result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); Value* resultHolder = protectPointer(jit, result); + setNodeValue(jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } break; From 066d350821b1093fadd04ee5f268a15795b08602 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 25 Jan 2015 15:25:03 +0600 Subject: [PATCH 083/290] Fixes indentation and adds missing const specifiers --- include/analysis.h | 11 ++++++++--- include/instructions.h | 13 ++++++++----- src/ControlGraph.cpp | 2 +- src/ControlGraphVisualizer.cpp | 6 +++--- src/ParsedBytecode.cpp | 2 +- src/ParsedMethod.cpp | 2 +- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 77e8dd7..43cad20 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -126,6 +126,7 @@ class ControlNode { // Get a list of nodes which refer current node as argument // Return true if at least one consumer is found bool getConsumers(TNodeList& result); + private: uint32_t m_index; TNodeType m_type; @@ -267,6 +268,7 @@ class ControlDomain { const TNodeList& getLocalStack() const { return m_localStack; } ControlDomain(BasicBlock* basicBlock) : m_entryPoint(0), m_terminator(0), m_basicBlock(basicBlock) { } + private: TNodeSet m_nodes; InstructionNode* m_entryPoint; @@ -280,7 +282,7 @@ class ControlDomain { class ControlGraph { public: ControlGraph(ParsedMethod* parsedMethod) - : m_parsedMethod(parsedMethod), m_lastNodeIndex(0), m_parsedBlock(0) { } + : m_parsedMethod(parsedMethod), m_parsedBlock(0), m_lastNodeIndex(0) { } ControlGraph(ParsedMethod* parsedMethod, ParsedBlock* parsedBlock) : m_parsedMethod(parsedMethod), m_parsedBlock(parsedBlock), m_lastNodeIndex(0) { } @@ -313,6 +315,9 @@ class ControlGraph { case ControlNode::ntTau: node = new TauNode(m_lastNodeIndex); break; + + default: + assert(false); } m_lastNodeIndex++; @@ -324,7 +329,7 @@ class ControlGraph { template T* newNode(); ControlDomain* newDomain(BasicBlock* basicBlock) { - ControlDomain* domain = new ControlDomain(basicBlock); + ControlDomain* const domain = new ControlDomain(basicBlock); m_domains.insert(domain); return domain; } @@ -402,7 +407,7 @@ class DomainVisitor { } protected: - ControlGraph* m_graph; + ControlGraph* const m_graph; }; class NodeVisitor : public DomainVisitor { diff --git a/include/instructions.h b/include/instructions.h index 7cd5035..984026e 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -55,6 +55,7 @@ struct TSmalltalkInstruction { bool isValueConsumer() const; std::string toString() const; + private: TOpcode m_opcode; TArgument m_argument; @@ -77,6 +78,7 @@ class InstructionDecoder { } static const TSmalltalkInstruction decodeAndShiftPointer(const TByteObject& byteCodes, uint16_t& bytePointer); + private: const TByteObject& m_byteCodes; uint16_t m_bytePointer; @@ -136,7 +138,7 @@ class BasicBlock { // Current block will hold instructions prior to the cut position // Returned block will hold the rest BasicBlock* split(const iterator& position) { - BasicBlock* newBlock = new BasicBlock; + BasicBlock* const newBlock = new BasicBlock; std::copy(position.get(), m_instructions.end(), newBlock->m_instructions.begin()); m_instructions.erase(position, m_instructions.end()); // TODO insert jump instruction and add newBlock to the parsed method @@ -163,6 +165,7 @@ class BasicBlock { } BasicBlock(uint16_t blockOffset = 0) : m_offset(blockOffset) { } + private: uint16_t m_offset; TInstructionVector m_instructions; @@ -208,7 +211,7 @@ class ParsedBytecode { virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset) = 0; protected: - TMethod* m_origin; + TMethod* const m_origin; TBasicBlockList m_basicBlocks; typedef std::map TOffsetToBasicBlockMap; @@ -286,7 +289,7 @@ class ParsedBlock : public ParsedBytecode { virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); protected: - ParsedMethod* m_containerMethod; + ParsedMethod* const m_containerMethod; uint16_t m_startOffset; uint16_t m_stopOffset; }; @@ -311,7 +314,7 @@ class BasicBlockVisitor { } protected: - ParsedBytecode* m_parsedBytecode; + ParsedBytecode* const m_parsedBytecode; }; class InstructionVisitor : public BasicBlockVisitor { @@ -356,7 +359,7 @@ class ParsedBlockVisitor { virtual bool visitBlock(ParsedBlock& parsedBlock) { return true; } private: - ParsedMethod* m_parsedMethod; + ParsedMethod* const m_parsedMethod; }; } // namespace st diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 8aa588f..eb12cdb 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -91,7 +91,7 @@ class GraphConstructor : public InstructionVisitor { void processSpecials(InstructionNode* node); void processPrimitives(InstructionNode* node); - ControlGraph* m_graph; + ControlGraph* const m_graph; ControlDomain* m_currentDomain; bool m_skipStubInstructions; }; diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 349e1e5..509e28b 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -12,8 +12,8 @@ bool ControlGraphVisualizer::visitDomain(st::ControlDomain& domain) { } std::string edgeStyle(st::ControlNode* from, st::ControlNode* to) { - const st::InstructionNode* fromInstruction = from->cast(); - const st::InstructionNode* toInstruction = to->cast(); + const st::InstructionNode* const fromInstruction = from->cast(); + const st::InstructionNode* const toInstruction = to->cast(); if ((fromInstruction && fromInstruction->getInstruction().isBranch()) || (toInstruction && toInstruction->getArgumentsCount() == 0)) @@ -47,7 +47,7 @@ bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { } // Processing argument edges - if (const st::InstructionNode* instruction = node.cast()) { + if (const st::InstructionNode* const instruction = node.cast()) { const std::size_t argsCount = instruction->getArgumentsCount(); for (std::size_t index = 0; index < argsCount; index++) { m_stream << "\t\t" << node.getIndex() << " -> " << instruction->getArgument(index)->getIndex() << " ["; diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 13ba5d9..9d4593b 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -146,7 +146,7 @@ BasicBlock* ParsedBytecode::createBasicBlock(uint16_t blockOffset) { // Creating the referred basic block and inserting it into the function // Later it will be filled with instructions and linked to other blocks - BasicBlock* newBasicBlock = new BasicBlock(blockOffset); + BasicBlock* const newBasicBlock = new BasicBlock(blockOffset); m_offsetToBasicBlock[blockOffset] = newBasicBlock; m_basicBlocks.push_back(newBasicBlock); diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 9b66aca..19a6c16 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -5,7 +5,7 @@ using namespace st; void ParsedMethod::parseBlock(uint16_t startOffset, uint16_t stopOffset) { // Following instruction belong to the nested code block // ParsedBlock will decode all of it's instructions and nested blocks - ParsedBlock* parsedBlock = new ParsedBlock(this, startOffset, stopOffset); + ParsedBlock* const parsedBlock = new ParsedBlock(this, startOffset, stopOffset); m_offsetToParsedBlock[startOffset] = parsedBlock; } From 916370f95c550830360a7f45b871ba74c9075ee3 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 25 Jan 2015 16:05:28 +0600 Subject: [PATCH 084/290] Adds message id to visualized SendMessage node --- src/ControlGraphVisualizer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 509e28b..161a9c1 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -87,6 +87,12 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { st::InstructionNode* instruction = node->cast(); label = instruction->getInstruction().toString(); + if (instruction->getInstruction().getOpcode() == opcode::sendMessage) { + TSymbolArray* const literals = m_graph->getParsedMethod()->getOrigin()->literals; + TSymbol* const name = literals->getField(instruction->getInstruction().getArgument()); + label += " " + name->toString(); + } + const bool isTerminator = instruction->getInstruction().isTerminator(); const bool isEntryPoint = (instruction == instruction->getDomain()->getEntryPoint()); From b369b4c10e9e7875146e6a9651196b722c632310 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 25 Jan 2015 20:56:46 +0600 Subject: [PATCH 085/290] Fixes graph linking issues --- include/analysis.h | 2 +- src/ControlGraph.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 43cad20..a44ba8d 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -251,7 +251,7 @@ class ControlDomain { void requestArgument(std::size_t index, InstructionNode* forNode) { if (! m_localStack.empty()) { ControlNode* argument = popValue(); - argument->addEdge(forNode); + //argument->addEdge(forNode); forNode->setArgument(index, argument); } else { m_reqestedArguments.push_back((TArgumentRequest){index, forNode}); diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index eb12cdb..4f2bbbc 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -303,7 +303,7 @@ class GraphLinker : public NodeVisitor { // Linking pending node if (m_nodeToLink) { - std::printf("GraphLinker::processNode : fallback linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); + std::printf("GraphLinker::processNode : linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); m_nodeToLink->addEdge(&node); m_nodeToLink = 0; } @@ -355,7 +355,12 @@ class GraphLinker : public NodeVisitor { ControlNode* const node = getRequestedNode(domain, argumentIndex); std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", node->getIndex(), request.requestingNode->getIndex()); - node->addEdge(request.requestingNode); + + // We need to link the nodes only from the same domain + // Cross domain references are handled separately + if (node->getDomain() == request.requestingNode->getDomain()) + node->addEdge(request.requestingNode); + request.requestingNode->setArgument(request.index, node); } From bc96a259fe2d4390cc87874996cad7190ed0c7ca Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 28 Jan 2015 10:25:01 +0600 Subject: [PATCH 086/290] Adds type inference thoughts and possible type expressions. --- doc/types.txt | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 doc/types.txt diff --git a/doc/types.txt b/doc/types.txt new file mode 100644 index 0000000..0ee7ac1 --- /dev/null +++ b/doc/types.txt @@ -0,0 +1,165 @@ +Типы, как монадическая категория + +Чистые функции без параметров: + + self::* -> self, 42 + foo + ^42 + + self::* -> self, (SmallInt) + rnd + ^SmallInt random + + +Чистые функции с параметрами: + +Прямое связывание типа аргумента с типом функции + id: param + ^param + +Ти́повое выражение: + self::*, param::* -> self, param + + +Функция с двумя параметрами: + sum: a and: b + ^ a + b + +Контекст object sum: intvara and: intvarb + self::*, (SmallInt), (SmallInt) -> self, (SmallInt) + +Контекст object sum: 2 and: 3 + self::*, a::2, b::3 -> self, 5 + +то есть, литеральные значения протаскиваются прямо через тип + + +Чистый аксессор для объекта с одним полем: + self::[_,_,field::*] -> self, field + field + ^field + +Примитивный аксессор класса вне контекста: + self::[_,class::*, ...] -> self, class + class + ^<2> "примитив getClass" + +В контексте Object new class ти́повое выражение будет: + self::[_,class::Class,_] -> self, class + +(здесь class — это литеральное значение объекта метакласса, соответствующее классу объекта Object) + + +«Чистый» мутатор (линза?): + self::[_,_,_], value::* -> self'::[_,_,value], self' + field: value + field <- value + +Функция с побочным эффектом на объект: + dirty: param + field <- param + ^param + 1 + +Вне контекста + self::[_,_,_], param::* -> self'::[_,_,param], * + +Контекст object dirty: intvar + self::[_,_,_], param::(SmallInt) -> self'::[_,_,param], (SmallInt) + +Контекст object dirty: 42 + self::[_,_,_], param::42 -> self'::[_,_,42], 43 + + + +А теперь самый вынос мозга — система контекста виртуальной машине — это монод в категории эндофункторов! Монада то бишь. +Переход виртуальной машины от одного состояния к другому — это монадическая операция. + +А это внезапно означает: + +1. Трансформации типов при выполнении императивной программы выражаются ФУНКЦИОНАЛЬНЫМИ отношениями!! + +Появляется понятие чистой ти́повой функции, когда тип результата чистым образом зависит от входных данных. +2. Паттерн матчинг! Ти́повые выражения можно рассматривать как шаблон подстановки или отображение типа аргументов в тип результата + +Можно описать трансформации типов в духе подстановочных функций Хаскеля. + +Например оператор * в общем случае подчиняется следующим шаблонам (сверху-вниз): + +Арифметические отношения (чистота self следует из определения): + self::(SmallInt), 0 -> 0 + self::(SmallInt), 1 -> self::(SmallInt) + +Отношения для монотипов и литералов в частном порядке: + self::(SmallInt), (SmallInt) -> self'::(SmallInt) + сюда подпадут выражения «(SmallInt) * 5» и «5 * (SmallInt)» и «2 * 3» + +Последним идет определение для общего случая: + self::*, (SmallInt) -> self'::* + + +Фишка в том, что в вырожденных случаях можно определить тип результата (вплоть до литерального!) даже не подставляя функцию. + +Монадические отношения в связи с объектом self позволяют формализовать тот факт, что при вызове метода поля самого объекта не меняются. +Это позволяет более смело выполнять оптимизации и связывать далекие участки графа без опасения что типы совпали «случайно». + + + +Типы методов: + - С побочными эффектами (грязный) + - Без побочных эффектов на поля + - С грязными блоками + - С чистыми блоками + - Без побочных эффектов (чистый) + +Имеем метод + test: a |x| + x <- 42. + a foo. + a bar: [ x <- false ]. + x <- 'Hello'. + a baz. + +Вне контекста типы можно вывести следующим образом +(указывается значение на момент сразу после выполнения строки): + x <- 42. "x :: 42" + a foo. "x :: 42" + a bar: [ x <- false ]. "x :: (42, false)" + x <- 'Hello'. "x :: (42, false, 'Hello')" + a baz. "x :: (42, false, 'Hello')" + + +Если анализ докажет, что блок не вызывается ни разу, то тип переменной x выводится литерально на диапазонах: + x <- 42. "x :: 42" + a foo. "x :: 42" + a bar: [ x <- false ]. "x :: 42" + x <- 'Hello'. "x :: 'Hello'" + a baz. "x :: 'Hello'" + + +Если анализ докажет, что блок вызывается только в пределах bar (то есть, bar не имеет побочных эффектов относительно блока), +то типы выводятся следующим образом: + + x <- 42. "x :: 42" + a foo. "x :: 42" + a bar: [ x <- false ]. "x :: (42, false)" + x <- 'Hello'. "x :: 'Hello'" + a baz. "x :: 'Hello'" + + +На основании полноты информации можно очертить диапазон возможных значений переменной при выполнении кода. +Даже в условиях композитного типа в некоторых случаях можно провести анализ и девиртуализацию посылки сообщения. +В вышеприведенном методе, даже в худшем случае может быть показано, что переменная не выйдет за пределы диапазона +(42, false, 'Hello'). Соответственно, последующие посылки сообщений объекту x можно выполнить с оптимизациями. + +В частности, если посылается сообщение с селектором, которому соответствует один и тот же метод для всех трех ветвей поиска, +то его можно вызвать в режиме «type erasure». При этом, все обращения к полям, примитивам или self обрабатываются динамически. +Бестиповая реализация, будучи неспециализированной, может эффективно разделяться между многими классами тем самым экономя ресурсы. + +В то же время, вызовы фундаментальных методов, таких как #respondsTo:, #class или #isKindOf: практически всегда могут быть +сведены к бестиповой форме. Конечно, она будет не такой эффективной как прямая специализация, но зато может позволить избежать +полиморфного кэша вызовов для данного сайта, что может упростить последующий анализ и облегчить инлайнинг. + +Более того, методы которые не содержат логики, зависящей от класса в контексте которого происходит вызов, следует по возможности +оформлять именно как бестиповые. Специализация не принесет ничего нового, только лишь увеличит накладные расходы. Если метод +не обращается к self, не работает с полями а только лишь обрабатывает аргументы и временные значения, то он с большой вероятностью +является кандидатом на генерализацию. Инлайнинг таких методов можно со спокойной душой отдать на откуп LLVM. From 876a18037d7b930df5aa5e488caf5efac362ff76 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 30 Jan 2015 20:37:04 +0600 Subject: [PATCH 087/290] Adds more thoughts on type inference --- doc/types.txt | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/doc/types.txt b/doc/types.txt index 0ee7ac1..6257f56 100644 --- a/doc/types.txt +++ b/doc/types.txt @@ -86,18 +86,19 @@ Например оператор * в общем случае подчиняется следующим шаблонам (сверху-вниз): Арифметические отношения (чистота self следует из определения): - self::(SmallInt), 0 -> 0 - self::(SmallInt), 1 -> self::(SmallInt) + self::(SmallInt), 0 -> self, 0 + self::(SmallInt), 1 -> self, (SmallInt) Отношения для монотипов и литералов в частном порядке: - self::(SmallInt), (SmallInt) -> self'::(SmallInt) - сюда подпадут выражения «(SmallInt) * 5» и «5 * (SmallInt)» и «2 * 3» + self::(SmallInt), (SmallInt) -> self, (SmallInt) + сюда попадут выражения «(SmallInt) * 5» и «5 * (SmallInt)» и «2 * 3» Последним идет определение для общего случая: - self::*, (SmallInt) -> self'::* + self::*, (SmallInt) -> self'::*, * Фишка в том, что в вырожденных случаях можно определить тип результата (вплоть до литерального!) даже не подставляя функцию. +Более того, для литеральных контекстов можно вычислить значение функции даже на этапе компиляции. Монадические отношения в связи с объектом self позволяют формализовать тот факт, что при вызове метода поля самого объекта не меняются. Это позволяет более смело выполнять оптимизации и связывать далекие участки графа без опасения что типы совпали «случайно». @@ -124,8 +125,8 @@ x <- 42. "x :: 42" a foo. "x :: 42" a bar: [ x <- false ]. "x :: (42, false)" - x <- 'Hello'. "x :: (42, false, 'Hello')" - a baz. "x :: (42, false, 'Hello')" + x <- 'Hello'. "x :: 'Hello'" + a baz. "x :: ('Hello', false)" Если анализ докажет, что блок не вызывается ни разу, то тип переменной x выводится литерально на диапазонах: @@ -152,14 +153,31 @@ (42, false, 'Hello'). Соответственно, последующие посылки сообщений объекту x можно выполнить с оптимизациями. В частности, если посылается сообщение с селектором, которому соответствует один и тот же метод для всех трех ветвей поиска, -то его можно вызвать в режиме «type erasure». При этом, все обращения к полям, примитивам или self обрабатываются динамически. -Бестиповая реализация, будучи неспециализированной, может эффективно разделяться между многими классами тем самым экономя ресурсы. +то его можно вызвать обобщенном режиме. При этом, все обращения к полям, примитивам, self/super обрабатываются динамически. +Обобщенная реализация, будучи неспециализированной, может эффективно разделяться между многими классами, тем самым экономя ресурсы. + +Например выражение x isNil может быть вычислено на этапе компиляции до литерального результата false, посколько для любой +из возможных ветвей результат будет идентичным (ни одна из ветвей не является потомком Undefined). В то же время, вызовы фундаментальных методов, таких как #respondsTo:, #class или #isKindOf: практически всегда могут быть -сведены к бестиповой форме. Конечно, она будет не такой эффективной как прямая специализация, но зато может позволить избежать +сведены к обобщенной форме. Конечно, она будет не такой эффективной как прямая специализация, но зато может позволить избежать полиморфного кэша вызовов для данного сайта, что может упростить последующий анализ и облегчить инлайнинг. Более того, методы которые не содержат логики, зависящей от класса в контексте которого происходит вызов, следует по возможности -оформлять именно как бестиповые. Специализация не принесет ничего нового, только лишь увеличит накладные расходы. Если метод -не обращается к self, не работает с полями а только лишь обрабатывает аргументы и временные значения, то он с большой вероятностью -является кандидатом на генерализацию. Инлайнинг таких методов можно со спокойной душой отдать на откуп LLVM. +оформлять именно как дженерики. Специализация не принесет ничего нового, только лишь увеличит накладные расходы. Если метод +не обращается к self и super, не работает с полями а только лишь обрабатывает аргументы и временные значения, то он с большой вероятностью +является кандидатом на генерализацию. Потенциальный инлайнинг таких методов можно со спокойной душой отдать на откуп LLVM. + +Тем не менее, обобщенные методы могут: + - Читать аргументы, с ограничением на self + - Разумеется, читать литералы + - Читать писать поля инстанции, читать в режиме * + - Читать и писать временные переменные + - Работать с блоками + +Отправка сообщений самому себе (self) и вверх по иерархии (super) возможна, +но реализуются динамически и с использованием полиморфного кэша. + +Впрочем, ветви кэша кодируются как вызовы специализированных версий методов, +поскольку в пределах ветви класс отправителя (self) становится известен. + From d8330ac011a237952820846e1f392429c7813bb2 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 31 Jan 2015 17:31:27 +0600 Subject: [PATCH 088/290] Fixes indentation in types.txt --- doc/types.txt | 147 +++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 72 deletions(-) diff --git a/doc/types.txt b/doc/types.txt index 6257f56..176c12d 100644 --- a/doc/types.txt +++ b/doc/types.txt @@ -2,72 +2,75 @@ Чистые функции без параметров: - self::* -> self, 42 - foo - ^42 + self::* -> self, 42 + foo + ^42 - self::* -> self, (SmallInt) - rnd - ^SmallInt random + self::* -> self, (SmallInt) + rnd + ^SmallInt random Чистые функции с параметрами: Прямое связывание типа аргумента с типом функции - id: param - ^param + id: param + ^param Ти́повое выражение: - self::*, param::* -> self, param + self::*, param::* -> self, param Функция с двумя параметрами: - sum: a and: b - ^ a + b + sum: a and: b + ^ a + b Контекст object sum: intvara and: intvarb - self::*, (SmallInt), (SmallInt) -> self, (SmallInt) + self::*, (SmallInt), (SmallInt) -> self, (SmallInt) Контекст object sum: 2 and: 3 - self::*, a::2, b::3 -> self, 5 + self::*, a::2, b::3 -> self, 5 + +Обобщенный метод + self::*, a::*, b::* -> self'::*, * то есть, литеральные значения протаскиваются прямо через тип Чистый аксессор для объекта с одним полем: - self::[_,_,field::*] -> self, field - field - ^field + self::[_,_,field::*] -> self, field + field + ^field Примитивный аксессор класса вне контекста: - self::[_,class::*, ...] -> self, class - class - ^<2> "примитив getClass" + self::[_,class::*, ...] -> self, class + class + ^<2> "примитив getClass" В контексте Object new class ти́повое выражение будет: - self::[_,class::Class,_] -> self, class + self::[_,class::Class,_] -> self, class (здесь class — это литеральное значение объекта метакласса, соответствующее классу объекта Object) «Чистый» мутатор (линза?): - self::[_,_,_], value::* -> self'::[_,_,value], self' - field: value - field <- value + self::[_,_,_], value::* -> self'::[_,_,value], self' + field: value + field <- value Функция с побочным эффектом на объект: - dirty: param - field <- param - ^param + 1 + dirty: param + field <- param + ^param + 1 Вне контекста - self::[_,_,_], param::* -> self'::[_,_,param], * + self::[_,_,_], param::* -> self'::[_,_,param], * Контекст object dirty: intvar - self::[_,_,_], param::(SmallInt) -> self'::[_,_,param], (SmallInt) + self::[_,_,_], param::(SmallInt) -> self'::[_,_,param], (SmallInt) Контекст object dirty: 42 - self::[_,_,_], param::42 -> self'::[_,_,42], 43 + self::[_,_,_], param::42 -> self'::[_,_,42], 43 @@ -86,15 +89,15 @@ Например оператор * в общем случае подчиняется следующим шаблонам (сверху-вниз): Арифметические отношения (чистота self следует из определения): - self::(SmallInt), 0 -> self, 0 - self::(SmallInt), 1 -> self, (SmallInt) + self::(SmallInt), 0 -> self, 0 + self::(SmallInt), 1 -> self, (SmallInt) Отношения для монотипов и литералов в частном порядке: - self::(SmallInt), (SmallInt) -> self, (SmallInt) - сюда попадут выражения «(SmallInt) * 5» и «5 * (SmallInt)» и «2 * 3» + self::(SmallInt), (SmallInt) -> self, (SmallInt) + сюда попадут выражения «(SmallInt) * 5» и «5 * (SmallInt)» и «2 * 3» Последним идет определение для общего случая: - self::*, (SmallInt) -> self'::*, * + self::*, (SmallInt) -> self'::*, * Фишка в том, что в вырожденных случаях можно определить тип результата (вплоть до литерального!) даже не подставляя функцию. @@ -106,57 +109,57 @@ Типы методов: - - С побочными эффектами (грязный) - - Без побочных эффектов на поля - - С грязными блоками - - С чистыми блоками - - Без побочных эффектов (чистый) + - С побочными эффектами (грязный) + - Без побочных эффектов на поля + - С грязными блоками + - С чистыми блоками + - Без побочных эффектов (чистый) Имеем метод - test: a |x| - x <- 42. - a foo. - a bar: [ x <- false ]. - x <- 'Hello'. - a baz. - -Вне контекста типы можно вывести следующим образом + test: a |x| + x <- 42. + a foo. + a bar: [ x <- false ]. + x <- 'Hello'. + a baz. + +Вне контекста типы можно вывести следующим образом (указывается значение на момент сразу после выполнения строки): - x <- 42. "x :: 42" - a foo. "x :: 42" - a bar: [ x <- false ]. "x :: (42, false)" - x <- 'Hello'. "x :: 'Hello'" - a baz. "x :: ('Hello', false)" + x <- 42. "x :: 42" + a foo. "x :: 42" + a bar: [ x <- false ]. "x :: (42, false)" + x <- 'Hello'. "x :: 'Hello'" + a baz. "x :: ('Hello', false)" Если анализ докажет, что блок не вызывается ни разу, то тип переменной x выводится литерально на диапазонах: - x <- 42. "x :: 42" - a foo. "x :: 42" - a bar: [ x <- false ]. "x :: 42" - x <- 'Hello'. "x :: 'Hello'" - a baz. "x :: 'Hello'" + x <- 42. "x :: 42" + a foo. "x :: 42" + a bar: [ x <- false ]. "x :: 42" + x <- 'Hello'. "x :: 'Hello'" + a baz. "x :: 'Hello'" Если анализ докажет, что блок вызывается только в пределах bar (то есть, bar не имеет побочных эффектов относительно блока), то типы выводятся следующим образом: - x <- 42. "x :: 42" - a foo. "x :: 42" - a bar: [ x <- false ]. "x :: (42, false)" - x <- 'Hello'. "x :: 'Hello'" - a baz. "x :: 'Hello'" + x <- 42. "x :: 42" + a foo. "x :: 42" + a bar: [ x <- false ]. "x :: (42, false)" + x <- 'Hello'. "x :: 'Hello'" + a baz. "x :: 'Hello'" -На основании полноты информации можно очертить диапазон возможных значений переменной при выполнении кода. +На основании полноты информации можно очертить диапазон возможных значений переменной при выполнении кода. Даже в условиях композитного типа в некоторых случаях можно провести анализ и девиртуализацию посылки сообщения. -В вышеприведенном методе, даже в худшем случае может быть показано, что переменная не выйдет за пределы диапазона +В вышеприведенном методе, даже в худшем случае может быть показано, что переменная не выйдет за пределы диапазона (42, false, 'Hello'). Соответственно, последующие посылки сообщений объекту x можно выполнить с оптимизациями. В частности, если посылается сообщение с селектором, которому соответствует один и тот же метод для всех трех ветвей поиска, то его можно вызвать обобщенном режиме. При этом, все обращения к полям, примитивам, self/super обрабатываются динамически. Обобщенная реализация, будучи неспециализированной, может эффективно разделяться между многими классами, тем самым экономя ресурсы. -Например выражение x isNil может быть вычислено на этапе компиляции до литерального результата false, посколько для любой +Например выражение x isNil может быть вычислено на этапе компиляции до литерального результата false, посколько для любой из возможных ветвей результат будет идентичным (ни одна из ветвей не является потомком Undefined). В то же время, вызовы фундаментальных методов, таких как #respondsTo:, #class или #isKindOf: практически всегда могут быть @@ -169,15 +172,15 @@ является кандидатом на генерализацию. Потенциальный инлайнинг таких методов можно со спокойной душой отдать на откуп LLVM. Тем не менее, обобщенные методы могут: - - Читать аргументы, с ограничением на self - - Разумеется, читать литералы - - Читать писать поля инстанции, читать в режиме * - - Читать и писать временные переменные - - Работать с блоками + - Читать аргументы, с ограничением на self + - Разумеется, читать литералы + - Писать поля инстанции, читать в режиме * + - Читать и писать временные переменные + - Работать с блоками Отправка сообщений самому себе (self) и вверх по иерархии (super) возможна, -но реализуются динамически и с использованием полиморфного кэша. +но реализуются динамически и с использованием полиморфного кэша. -Впрочем, ветви кэша кодируются как вызовы специализированных версий методов, +Впрочем, ветви кэша кодируются как вызовы специализированных версий методов, поскольку в пределах ветви класс отправителя (self) становится известен. From 641eb17f99440d6bde009fdbb1ecb4a456abdf15 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 31 Jan 2015 20:05:33 +0600 Subject: [PATCH 089/290] Adds implementation of GraphOptimizer::removeNode() --- include/analysis.h | 10 ++++++++++ src/ControlGraph.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/include/analysis.h b/include/analysis.h index a44ba8d..0067edb 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -334,6 +334,16 @@ class ControlGraph { return domain; } + void removeNode(ControlNode* node) { + // We allow to erase only orphan nodes + assert(node); + assert(!node->getInEdges().size()); + assert(!node->getOutEdges().size()); + + m_nodes.remove(node); + delete node; + } + ~ControlGraph() { TDomainSet::iterator iDomain = m_domains.begin(); while (iDomain != m_domains.end()) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 4f2bbbc..8e4eb18 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -461,7 +461,47 @@ class GraphOptimizer : public NodeVisitor { } private: void removeNode(ControlNode* node) { - // TODO + // Trivial instructions should have only one outgoing edge + assert(node->getOutEdges().size() == 1); + + ControlNode* const nextNode = * node->getOutEdges().begin(); + assert(nextNode && nextNode->getNodeType() == ControlNode::ntInstruction); + + // Fixing domain entry point + ControlDomain* const domain = node->getDomain(); + if (domain->getEntryPoint() == node) + domain->setEntryPoint(nextNode->cast()); + + // Fixing incoming edges by remapping them to the next node + TNodeSet::iterator iNode = node->getInEdges().begin(); + for (; iNode != node->getInEdges().end(); ++iNode) { + ControlNode* const sourceNode = *iNode; + + std::printf("Remapping node %.2u from %.2u to %.2u\n", + sourceNode->getIndex(), + node->getIndex(), + nextNode->getIndex()); + + sourceNode->removeEdge(node); + sourceNode->addEdge(nextNode); + } + + // Erasing outgoing edges + iNode = node->getOutEdges().begin(); + for (; iNode != node->getOutEdges().end(); ++iNode) + { + ControlNode* const targetNode = *iNode; + + std::printf("Erasing edge %.2u -> %.2u\n", + node->getIndex(), + targetNode->getIndex()); + + node->removeEdge(targetNode); + } + + // Removing node from the graph + node->getDomain()->removeNode(node); + m_graph->removeNode(node); } private: From 6f265306bc483b0eb7462125c7223e81e98cf80d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 1 Feb 2015 02:06:13 +0300 Subject: [PATCH 090/290] Changes the declaration of ControlNode::getConsumers() --- include/analysis.h | 3 +-- src/ControlGraph.cpp | 10 +++++----- src/MethodCompiler.cpp | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 0067edb..d6395f8 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -124,8 +124,7 @@ class ControlNode { llvm::Value* getValue() const { return m_value; } // Get a list of nodes which refer current node as argument - // Return true if at least one consumer is found - bool getConsumers(TNodeList& result); + TNodeList getConsumers() const; private: uint32_t m_index; diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 8e4eb18..3c0dd7c 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -12,8 +12,8 @@ bool DomainOffsetCompare::operator() (const ControlDomain* a, const ControlDomai return a->getBasicBlock()->getOffset() < b->getBasicBlock()->getOffset(); } -bool ControlNode::getConsumers(TNodeList& result) { - result.clear(); +TNodeList ControlNode::getConsumers() const { + TNodeList result; TNodeSet::iterator iNode = m_outEdges.begin(); for (; iNode != m_outEdges.end(); ++iNode) { @@ -26,7 +26,7 @@ bool ControlNode::getConsumers(TNodeList& result) { } } - return !result.empty(); + return result; } template<> InstructionNode* ControlNode::cast() { return this->getNodeType() == ntInstruction ? static_cast(this) : 0; } @@ -432,8 +432,8 @@ class GraphOptimizer : public NodeVisitor { if (!nodeInstruction.isTrivial() || !nodeInstruction.isValueProvider()) return NodeVisitor::visitNode(node); - TNodeList consumers; - if (! instruction->getConsumers(consumers)) { + const TNodeList& consumers = instruction->getConsumers(); + if (consumers.empty()) { std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); m_nodesToRemove.push_back(instruction); } else if (consumers.size() == 1) { diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index f3b80a0..b725520 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -402,8 +402,8 @@ void MethodCompiler::doPushInstance(TJITContext& jit) void MethodCompiler::doPushArgument(TJITContext& jit) { - /* st::TNodeList consumers; - if (!jit.currentNode->getConsumers(consumers)) { + /* const st::TNodeList& consumers = jit.currentNode->getConsumers(); + if (consumers.empty()) { assert(false); return; } From 0b3ceb2b953c6a9cfa8eebbc8eab7a56647dcc55 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 1 Feb 2015 02:23:59 +0300 Subject: [PATCH 091/290] Fixes memleak in ParsedMethod::parseBlock() --- src/ParsedMethod.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 19a6c16..ffda867 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -24,4 +24,9 @@ ParsedMethod::~ParsedMethod() { { delete * iBlock; } + for (TOffsetToParsedBlockMap::iterator iBlock = m_offsetToParsedBlock.begin(), + end = m_offsetToParsedBlock.end(); iBlock != end; ++iBlock) + { + delete iBlock->second; + } } From e1eb5910d64d76aaa1e23c8058ea3dd8214daae2 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 1 Feb 2015 02:34:38 +0300 Subject: [PATCH 092/290] Prettifies st::BasicBlock::iterator with typedef --- include/instructions.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index 984026e..75e516b 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -90,15 +90,16 @@ class BasicBlock { typedef std::set TBasicBlockSet; class iterator : public TInstructionVector::iterator { + typedef TInstructionVector::iterator parent; public: - iterator(const TInstructionVector::iterator& copy) : TInstructionVector::iterator(copy) { } + iterator(const parent& copy) : parent(copy) { } const TSmalltalkInstruction operator *() const { - return TSmalltalkInstruction(TInstructionVector::iterator::operator*()); + return TSmalltalkInstruction(parent::operator*()); } - TInstructionVector::iterator& get() { return static_cast(*this); } - const TInstructionVector::iterator& get() const { return static_cast(*this); } + parent& get() { return static_cast(*this); } + const parent& get() const { return static_cast(*this); } }; iterator begin() { return iterator(m_instructions.begin()); } From b0e1a78f7969997fdf42560d79a0c87429b6731b Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 1 Feb 2015 04:50:22 +0300 Subject: [PATCH 093/290] Refactors naming in ControlNode::addEdge() and removeEdge() --- include/analysis.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index d6395f8..5cb1b19 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -110,14 +110,14 @@ class ControlNode { TNodeSet& getInEdges() { return m_inEdges; } TNodeSet& getOutEdges() { return m_outEdges; } - void addEdge(ControlNode* to) { - m_outEdges.insert(to); - to->getInEdges().insert(this); + void addEdge(ControlNode* dest) { + this->m_outEdges.insert(dest); + dest->m_inEdges.insert(this); } - void removeEdge(ControlNode* to) { - m_outEdges.erase(to); - to->getInEdges().erase(this); + void removeEdge(ControlNode* dest) { + this->m_outEdges.erase(dest); + dest->m_inEdges.erase(this); } void setValue(llvm::Value* value) { m_value = value; } From 0f117ab4810969a729567652c63077e2b30e0efe Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 1 Feb 2015 19:21:36 +0600 Subject: [PATCH 094/290] Adds phi merging code --- include/analysis.h | 7 +- src/ControlGraph.cpp | 165 ++++++++++++++++++++++++++----------------- 2 files changed, 104 insertions(+), 68 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 5cb1b19..d341918 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -250,8 +250,11 @@ class ControlDomain { void requestArgument(std::size_t index, InstructionNode* forNode) { if (! m_localStack.empty()) { ControlNode* argument = popValue(); - //argument->addEdge(forNode); forNode->setArgument(index, argument); + + if (argument->getNodeType() == ControlNode::ntPhi) + argument->addEdge(forNode); + } else { m_reqestedArguments.push_back((TArgumentRequest){index, forNode}); } @@ -333,7 +336,7 @@ class ControlGraph { return domain; } - void removeNode(ControlNode* node) { + void eraseNode(ControlNode* node) { // We allow to erase only orphan nodes assert(node); assert(!node->getInEdges().size()); diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 3c0dd7c..24b7292 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -296,79 +296,105 @@ class GraphLinker : public NodeVisitor { } private: - void processNode(ControlNode& node) { - // In order to keep graph strongly connected, we link - // node to the next one. This edge would be interpreted - // as a control flow edge, not the stack value flow edge. - - // Linking pending node - if (m_nodeToLink) { - std::printf("GraphLinker::processNode : linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); - m_nodeToLink->addEdge(&node); - m_nodeToLink = 0; - } + void processNode(ControlNode& node); + void processBranching(); + void processArgumentRequests(); + void processRequest(ControlDomain* domain, std::size_t argumentIndex, const ControlDomain::TArgumentRequest& request); - InstructionNode* const instruction = node.cast(); - if (instruction && instruction->getInstruction().isTerminator()) - return; // terminator nodes will take care of themselves + void mergePhi(PhiNode* source, PhiNode* target); + ControlNode* getRequestedNode(ControlDomain* domain, std::size_t index); - TNodeSet& outEdges = node.getOutEdges(); - TNodeSet::iterator iNode = outEdges.begin(); - bool isNodeLinked = false; - for (; iNode != outEdges.end(); ++iNode) { - // Checking for connectivity - if ((*iNode)->getDomain() == node.getDomain() && (*iNode)->getIndex() > node.getIndex()) { - // Node is linked. No need to worry. - isNodeLinked = true; - break; - } - } + ControlDomain* m_currentDomain; + ControlNode* m_nodeToLink; +}; - if (! isNodeLinked) - m_nodeToLink = &node; +void GraphLinker::processNode(ControlNode& node) +{ + // In order to keep graph strongly connected, we link + // node to the next one. This edge would be interpreted + // as a control flow edge, not the stack value flow edge. + + // Linking pending node + if (m_nodeToLink) { + std::printf("GraphLinker::processNode : linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); + m_nodeToLink->addEdge(&node); + m_nodeToLink = 0; } - void processBranching() { - // Current domain's entry point should be linked to the terminators of referring domains - InstructionNode* const entryPoint = m_currentDomain->getEntryPoint(); - assert(entryPoint); + InstructionNode* const instruction = node.cast(); + if (instruction && instruction->getInstruction().isTerminator()) + return; // terminator nodes will take care of themselves - const BasicBlock::TBasicBlockSet& referers = m_currentDomain->getBasicBlock()->getReferers(); - BasicBlock::TBasicBlockSet::iterator iReferer = referers.begin(); - for (; iReferer != referers.end(); ++iReferer) { - ControlDomain* const refererDomain = m_graph->getDomainFor(*iReferer); - InstructionNode* const terminator = refererDomain->getTerminator(); - assert(terminator && terminator->getInstruction().isBranch()); - - std::printf("GraphLinker::processNode : linking nodes of referring graphs %.2u and %.2u\n", terminator->getIndex(), entryPoint->getIndex()); - terminator->addEdge(entryPoint); + TNodeSet& outEdges = node.getOutEdges(); + TNodeSet::iterator iNode = outEdges.begin(); + bool isNodeLinked = false; + for (; iNode != outEdges.end(); ++iNode) { + // Checking for connectivity + if ((*iNode)->getDomain() == node.getDomain() && (*iNode)->getIndex() > node.getIndex()) { + // Node is linked. No need to worry. + isNodeLinked = true; + break; } } - void processArgumentRequests() { - const ControlDomain::TRequestList& requestList = m_currentDomain->getRequestedArguments(); - for (std::size_t index = 0; index < requestList.size(); index++) - processRequest(m_currentDomain, index, requestList[index]); + if (! isNodeLinked) + m_nodeToLink = &node; +} + +void GraphLinker::processBranching() +{ + // Current domain's entry point should be linked to the terminators of referring domains + InstructionNode* const entryPoint = m_currentDomain->getEntryPoint(); + assert(entryPoint); + + const BasicBlock::TBasicBlockSet& referers = m_currentDomain->getBasicBlock()->getReferers(); + BasicBlock::TBasicBlockSet::iterator iReferer = referers.begin(); + for (; iReferer != referers.end(); ++iReferer) { + ControlDomain* const refererDomain = m_graph->getDomainFor(*iReferer); + InstructionNode* const terminator = refererDomain->getTerminator(); + assert(terminator && terminator->getInstruction().isBranch()); + + std::printf("GraphLinker::processNode : linking nodes of referring graphs %.2u and %.2u\n", terminator->getIndex(), entryPoint->getIndex()); + terminator->addEdge(entryPoint); } +} + +void GraphLinker::processArgumentRequests() +{ + const ControlDomain::TRequestList& requestList = m_currentDomain->getRequestedArguments(); + for (std::size_t index = 0; index < requestList.size(); index++) + processRequest(m_currentDomain, index, requestList[index]); +} - void processRequest(ControlDomain* domain, std::size_t argumentIndex, const ControlDomain::TArgumentRequest& request) { - ControlNode* const node = getRequestedNode(domain, argumentIndex); +void GraphLinker::processRequest(ControlDomain* domain, std::size_t argumentIndex, const ControlDomain::TArgumentRequest& request) +{ + ControlNode* const argument = getRequestedNode(domain, argumentIndex); + const ControlNode::TNodeType argumentType = argument->getNodeType(); - std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", node->getIndex(), request.requestingNode->getIndex()); + std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", argument->getIndex(), request.requestingNode->getIndex()); - // We need to link the nodes only from the same domain - // Cross domain references are handled separately - if (node->getDomain() == request.requestingNode->getDomain()) - node->addEdge(request.requestingNode); + request.requestingNode->setArgument(request.index, argument); - request.requestingNode->setArgument(request.index, node); - } + // We need to link the nodes only from the same domain + // Cross domain references are handled separately + if (argument->getDomain() == request.requestingNode->getDomain() || argumentType == ControlNode::ntPhi) + argument->addEdge(request.requestingNode); +} - ControlNode* getRequestedNode(ControlDomain* domain, std::size_t index); +void GraphLinker::mergePhi(PhiNode* source, PhiNode* target) +{ + // All incoming edges of source node became incoming edges of target node. + TNodeSet::iterator iEdge = source->getInEdges().begin(); + while (iEdge != source->getInEdges().end()) { + ControlNode* const argument = *iEdge++; - ControlDomain* m_currentDomain; - ControlNode* m_nodeToLink; -}; + argument->removeEdge(source); + argument->addEdge(target); + } + + // Deleting source node because it is no longer used + m_graph->eraseNode(source); +} ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t argumentIndex) { @@ -392,10 +418,15 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar assert(newIndex <= argumentIndex); ControlNode* const refererValue = getRequestedNode(refererDomain, newIndex); - if (singleReferer) + if (singleReferer) { result = refererValue; - else - refererValue->addEdge(result); + } else { + // Nested phi nodes should be merged together + if (PhiNode* const phi = refererValue->cast()) + mergePhi(phi, static_cast(result)); + else + refererValue->addEdge(result); + } } else { const std::size_t valueIndex = refererStackSize - 1 - argumentIndex; @@ -415,7 +446,7 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar class GraphOptimizer : public NodeVisitor { public: - GraphOptimizer(ControlGraph* graph) : NodeVisitor(graph) {} + GraphOptimizer(ControlGraph* graph) : NodeVisitor(graph), m_currentDomain(0) {} virtual bool visitDomain(ControlDomain& domain) { m_currentDomain = &domain; @@ -455,10 +486,12 @@ class GraphOptimizer : public NodeVisitor { } virtual void domainsVisited() { + // Removing nodes that were optimized out TNodeList::iterator iNode = m_nodesToRemove.begin(); for (; iNode != m_nodesToRemove.end(); ++iNode) removeNode(*iNode); } + private: void removeNode(ControlNode* node) { // Trivial instructions should have only one outgoing edge @@ -474,8 +507,8 @@ class GraphOptimizer : public NodeVisitor { // Fixing incoming edges by remapping them to the next node TNodeSet::iterator iNode = node->getInEdges().begin(); - for (; iNode != node->getInEdges().end(); ++iNode) { - ControlNode* const sourceNode = *iNode; + while (iNode != node->getInEdges().end()) { + ControlNode* const sourceNode = *iNode++; std::printf("Remapping node %.2u from %.2u to %.2u\n", sourceNode->getIndex(), @@ -488,9 +521,9 @@ class GraphOptimizer : public NodeVisitor { // Erasing outgoing edges iNode = node->getOutEdges().begin(); - for (; iNode != node->getOutEdges().end(); ++iNode) + while (iNode != node->getOutEdges().end()) { - ControlNode* const targetNode = *iNode; + ControlNode* const targetNode = *iNode++; std::printf("Erasing edge %.2u -> %.2u\n", node->getIndex(), @@ -501,7 +534,7 @@ class GraphOptimizer : public NodeVisitor { // Removing node from the graph node->getDomain()->removeNode(node); - m_graph->removeNode(node); + m_graph->eraseNode(node); } private: From b5085bd4f5b536a031d54adf2d0e7712306b6be9 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 4 Jul 2014 21:08:03 +0400 Subject: [PATCH 095/290] Fixes -Wreorder warning Issue: #32 --- include/analysis.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index d341918..114548f 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -284,7 +284,7 @@ class ControlDomain { class ControlGraph { public: ControlGraph(ParsedMethod* parsedMethod) - : m_parsedMethod(parsedMethod), m_parsedBlock(0), m_lastNodeIndex(0) { } + : m_parsedMethod(parsedMethod), m_parsedBlock(0), m_lastNodeIndex(0) { } ControlGraph(ParsedMethod* parsedMethod, ParsedBlock* parsedBlock) : m_parsedMethod(parsedMethod), m_parsedBlock(parsedBlock), m_lastNodeIndex(0) { } @@ -380,7 +380,7 @@ class ControlGraph { uint32_t m_lastNodeIndex; typedef std::map TDomainMap; - TDomainMap m_blocksToDomains; + TDomainMap m_blocksToDomains; }; template<> InstructionNode* ControlNode::cast(); From ab3a7b05de8e44b00388d0ae0979d2247da4779d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 2 Feb 2015 11:07:04 +0600 Subject: [PATCH 096/290] Fixes graph optimizer --- include/analysis.h | 11 ++++++++--- src/ControlGraph.cpp | 23 ++++------------------- src/ControlGraphVisualizer.cpp | 2 +- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 114548f..12dd32e 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -65,6 +65,7 @@ class BlockReturnDetector : public ParsedBlockVisitor { class ControlDomain; class ControlNode; +class InstructionNode; typedef std::vector TNodeList; @@ -107,8 +108,8 @@ class ControlNode { ControlDomain* getDomain() const { return m_domain; } void setDomain(ControlDomain* value) { m_domain = value; } - TNodeSet& getInEdges() { return m_inEdges; } - TNodeSet& getOutEdges() { return m_outEdges; } + const TNodeSet& getInEdges() const { return m_inEdges; } + const TNodeSet& getOutEdges() const { return m_outEdges; } void addEdge(ControlNode* dest) { this->m_outEdges.insert(dest); @@ -124,7 +125,9 @@ class ControlNode { llvm::Value* getValue() const { return m_value; } // Get a list of nodes which refer current node as argument - TNodeList getConsumers() const; + void addConsumer(ControlNode* consumer) { m_consumers.insert(consumer); } + void removeConsumer(ControlNode* consumer) { m_consumers.erase(consumer); } + const TNodeSet& getConsumers() const { return m_consumers; } private: uint32_t m_index; @@ -134,6 +137,7 @@ class ControlNode { ControlDomain* m_domain; llvm::Value* m_value; + TNodeSet m_consumers; }; // Instruction node represents a signle VM instruction and it's relations in code. @@ -251,6 +255,7 @@ class ControlDomain { if (! m_localStack.empty()) { ControlNode* argument = popValue(); forNode->setArgument(index, argument); + argument->addConsumer(forNode); if (argument->getNodeType() == ControlNode::ntPhi) argument->addEdge(forNode); diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 24b7292..f074171 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -12,23 +12,6 @@ bool DomainOffsetCompare::operator() (const ControlDomain* a, const ControlDomai return a->getBasicBlock()->getOffset() < b->getBasicBlock()->getOffset(); } -TNodeList ControlNode::getConsumers() const { - TNodeList result; - - TNodeSet::iterator iNode = m_outEdges.begin(); - for (; iNode != m_outEdges.end(); ++iNode) { - if (InstructionNode* const instruction = (*iNode)->cast()) { - for (std::size_t index = 0; index < instruction->getArgumentsCount(); index++) - if (instruction->getArgument(index) == this) - result.push_back(instruction); - } else if (PhiNode* const phi = (*iNode)->cast()) { - result.push_back(phi); - } - } - - return result; -} - template<> InstructionNode* ControlNode::cast() { return this->getNodeType() == ntInstruction ? static_cast(this) : 0; } template<> PhiNode* ControlNode::cast() { return this->getNodeType() == ntPhi ? static_cast(this) : 0; } template<> TauNode* ControlNode::cast() { return this->getNodeType() == ntTau ? static_cast(this) : 0; } @@ -325,7 +308,7 @@ void GraphLinker::processNode(ControlNode& node) if (instruction && instruction->getInstruction().isTerminator()) return; // terminator nodes will take care of themselves - TNodeSet& outEdges = node.getOutEdges(); + const TNodeSet& outEdges = node.getOutEdges(); TNodeSet::iterator iNode = outEdges.begin(); bool isNodeLinked = false; for (; iNode != outEdges.end(); ++iNode) { @@ -374,6 +357,7 @@ void GraphLinker::processRequest(ControlDomain* domain, std::size_t argumentInde std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", argument->getIndex(), request.requestingNode->getIndex()); request.requestingNode->setArgument(request.index, argument); + argument->addConsumer(request.requestingNode); // We need to link the nodes only from the same domain // Cross domain references are handled separately @@ -463,7 +447,7 @@ class GraphOptimizer : public NodeVisitor { if (!nodeInstruction.isTrivial() || !nodeInstruction.isValueProvider()) return NodeVisitor::visitNode(node); - const TNodeList& consumers = instruction->getConsumers(); + const TNodeSet& consumers = instruction->getConsumers(); if (consumers.empty()) { std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); m_nodesToRemove.push_back(instruction); @@ -533,6 +517,7 @@ class GraphOptimizer : public NodeVisitor { } // Removing node from the graph + std::printf("Erasing node %.2u\n", node->getIndex()); node->getDomain()->removeNode(node); m_graph->eraseNode(node); } diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 161a9c1..24aae53 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -97,7 +97,7 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { const bool isEntryPoint = (instruction == instruction->getDomain()->getEntryPoint()); if (isTerminator && isEntryPoint) - color = "green3;red"; + color = "red"; // color = "green3;red"; else if (isEntryPoint) color = "green3"; else if (isTerminator) From 478560413eca9d4ef2517de0f836e6f767b397b9 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 2 Feb 2015 22:15:49 +0600 Subject: [PATCH 097/290] Fixes consumer registration in case of phi nodes --- src/ControlGraph.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index f074171..a6d0ded 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -361,8 +361,17 @@ void GraphLinker::processRequest(ControlDomain* domain, std::size_t argumentInde // We need to link the nodes only from the same domain // Cross domain references are handled separately - if (argument->getDomain() == request.requestingNode->getDomain() || argumentType == ControlNode::ntPhi) + if (argument->getDomain() == request.requestingNode->getDomain()) argument->addEdge(request.requestingNode); + + if (argumentType == ControlNode::ntPhi) { + argument->addEdge(request.requestingNode); + + // Registering phi as a consumer for all input nodes + TNodeSet::iterator iNode = argument->getInEdges().begin(); + for (; iNode != argument->getInEdges().end(); ++iNode) + (*iNode)->addConsumer(argument); + } } void GraphLinker::mergePhi(PhiNode* source, PhiNode* target) From db18adab0460349a4c58659e6c3ea697b6c63c62 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 3 Feb 2015 22:04:35 +0600 Subject: [PATCH 098/290] Adds implementation of TSmalltalkInstruction::isValueConsumer() --- src/TSmalltalkInstruction.cpp | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 34ff81e..acef4e7 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -101,7 +101,38 @@ bool st::TSmalltalkInstruction::isTrivial() const { } bool st::TSmalltalkInstruction::isValueConsumer() const { - assert(false); // TODO + switch (m_opcode) { + case opcode::pushInstance: + case opcode::pushArgument: + case opcode::pushTemporary: + case opcode::pushLiteral: + case opcode::pushConstant: + case opcode::pushBlock: + return false; + + case opcode::assignTemporary: + case opcode::assignInstance: + case opcode::sendUnary: + case opcode::sendBinary: + case opcode::sendMessage: + case opcode::markArguments: + return true; + + case opcode::doSpecial: + // All other specials consume a value + return m_argument != special::branch; + + case opcode::doPrimitive: + // All system primitives consume a value + // TODO User defined primitives + return true; + + case opcode::extended: + default: + assert(false); + } + + assert(false); return false; } From 7eb91fede7e3a8de2d82bb91b7a06550f9310dae Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 3 Feb 2015 23:22:08 +0600 Subject: [PATCH 099/290] Adds logic to eliminate redundant phis --- src/ControlGraph.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index a6d0ded..b0c0661 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -473,6 +473,11 @@ class GraphOptimizer : public NodeVisitor { } } } + } else if (PhiNode* const phi = node.cast()) { + if (phi->getInEdges().size() == 1) { + std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", instruction->getIndex()); + m_nodesToRemove.push_back(phi); + } } return NodeVisitor::visitNode(node); @@ -481,12 +486,38 @@ class GraphOptimizer : public NodeVisitor { virtual void domainsVisited() { // Removing nodes that were optimized out TNodeList::iterator iNode = m_nodesToRemove.begin(); - for (; iNode != m_nodesToRemove.end(); ++iNode) - removeNode(*iNode); + for (; iNode != m_nodesToRemove.end(); ++iNode) { + if (InstructionNode* const instruction = (*iNode)->cast()) + removeInstruction(instruction); + else if (PhiNode* const phi = (*iNode)->cast()) + removePhi(phi); + else + assert(false); + } } private: - void removeNode(ControlNode* node) { + void removePhi(PhiNode* phi) { + assert(phi->getInEdges().size() == 1); + + ControlNode* const valueSource = *phi->getInEdges().begin(); + ControlNode* const valueTarget = *phi->getOutEdges().begin(); + + std::printf("Skipping phi node %.2u and remapping edges to link values directly: %.2u <-- %.2u\n", + phi->getIndex(), + valueSource->getIndex(), + valueTarget->getIndex()); + + valueSource->removeEdge(phi); + phi->removeEdge(valueTarget); + + valueSource->removeConsumer(phi); + valueSource->addConsumer(valueTarget); + + m_graph->eraseNode(phi); + } + + void removeInstruction(InstructionNode* node) { // Trivial instructions should have only one outgoing edge assert(node->getOutEdges().size() == 1); From 9d90ed42103a2a00348ff14abfec75af5f383aa8 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 4 Feb 2015 00:04:32 +0600 Subject: [PATCH 100/290] Adds SimpleNodeVisitor and fixes GraphOptimizer --- include/analysis.h | 32 +++++++++++++++++++++++++++++++- src/ControlGraph.cpp | 20 +++++++------------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 12dd32e..0f5532f 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -307,6 +307,11 @@ class ControlGraph { iterator begin() { return m_domains.begin(); } iterator end() { return m_domains.end(); } + typedef std::list TNodeList; + typedef TNodeList::iterator nodes_iterator; + nodes_iterator nodes_begin() { return m_nodes.begin(); } + nodes_iterator nodes_end() { return m_nodes.end(); } + ControlNode* newNode(ControlNode::TNodeType type) { ControlNode* node = 0; @@ -380,7 +385,6 @@ class ControlGraph { ParsedBlock* m_parsedBlock; TDomainSet m_domains; - typedef std::list TNodeList; TNodeList m_nodes; uint32_t m_lastNodeIndex; @@ -453,6 +457,32 @@ class NodeVisitor : public DomainVisitor { } }; +class PlainNodeVisitor { +public: + PlainNodeVisitor(ControlGraph* graph) : m_graph(graph) { } + virtual bool visitNode(ControlNode& node) { return true; } + virtual void nodesVisited() { } + + void run() { + ControlGraph::nodes_iterator iNode = m_graph->nodes_begin(); + const ControlGraph::nodes_iterator iEnd = m_graph->nodes_end(); + + if (iNode != iEnd) { + while (iNode != iEnd) { + if (! visitNode(** iNode)) + break; + + ++iNode; + } + + nodesVisited(); + } + } + +protected: + ControlGraph* const m_graph; +}; + } // namespace st #endif diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index b0c0661..c4fcba6 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -437,14 +437,9 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar return result; } -class GraphOptimizer : public NodeVisitor { +class GraphOptimizer : public PlainNodeVisitor { public: - GraphOptimizer(ControlGraph* graph) : NodeVisitor(graph), m_currentDomain(0) {} - - virtual bool visitDomain(ControlDomain& domain) { - m_currentDomain = &domain; - return NodeVisitor::visitDomain(domain); - } + GraphOptimizer(ControlGraph* graph) : PlainNodeVisitor(graph) {} virtual bool visitNode(ControlNode& node) { // If node pushes value on the stack but this value is not consumed @@ -454,7 +449,7 @@ class GraphOptimizer : public NodeVisitor { if (InstructionNode* const instruction = node.cast()) { const TSmalltalkInstruction& nodeInstruction = instruction->getInstruction(); if (!nodeInstruction.isTrivial() || !nodeInstruction.isValueProvider()) - return NodeVisitor::visitNode(node); + return true; const TNodeSet& consumers = instruction->getConsumers(); if (consumers.empty()) { @@ -475,15 +470,15 @@ class GraphOptimizer : public NodeVisitor { } } else if (PhiNode* const phi = node.cast()) { if (phi->getInEdges().size() == 1) { - std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", instruction->getIndex()); + std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", phi->getIndex()); m_nodesToRemove.push_back(phi); } } - return NodeVisitor::visitNode(node); + return true; } - virtual void domainsVisited() { + virtual void nodesVisited() { // Removing nodes that were optimized out TNodeList::iterator iNode = m_nodesToRemove.begin(); for (; iNode != m_nodesToRemove.end(); ++iNode) { @@ -563,8 +558,7 @@ class GraphOptimizer : public NodeVisitor { } private: - TNodeList m_nodesToRemove; - ControlDomain* m_currentDomain; + TNodeList m_nodesToRemove; }; void ControlGraph::buildGraph() From fb2b907310c0a00f47bc64e8ffa44af1dcbd3b0a Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 4 Feb 2015 10:31:49 +0600 Subject: [PATCH 101/290] Fixes removePhi --- src/ControlGraph.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index c4fcba6..5122f8b 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -496,7 +496,8 @@ class GraphOptimizer : public PlainNodeVisitor { assert(phi->getInEdges().size() == 1); ControlNode* const valueSource = *phi->getInEdges().begin(); - ControlNode* const valueTarget = *phi->getOutEdges().begin(); + InstructionNode* const valueTarget = (*phi->getOutEdges().begin())->cast(); + assert(valueTarget); std::printf("Skipping phi node %.2u and remapping edges to link values directly: %.2u <-- %.2u\n", phi->getIndex(), @@ -507,7 +508,9 @@ class GraphOptimizer : public PlainNodeVisitor { phi->removeEdge(valueTarget); valueSource->removeConsumer(phi); + valueSource->addConsumer(valueTarget); + valueTarget->setArgument(phi->getPhiIndex(), valueSource); m_graph->eraseNode(phi); } From e17e1d0de44d1b631ba8684e7ff384498dceffdd Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 5 Feb 2015 23:27:57 +0600 Subject: [PATCH 102/290] Fixes GraphConstructor --- src/ControlGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 5122f8b..4fd6152 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -158,13 +158,13 @@ void GraphConstructor::processSpecials(InstructionNode* node) switch (instruction.getArgument()) { case special::stackReturn: case special::blockReturn: - case special::sendToSuper: m_currentDomain->requestArgument(0, node); assert(! m_currentDomain->getTerminator()); m_currentDomain->setTerminator(node); break; + case special::sendToSuper: case special::duplicate: m_currentDomain->requestArgument(0, node); m_currentDomain->pushValue(node); From 0f2cbb1207c267c604eff952f064b7d8d1d0c34c Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 6 Feb 2015 11:32:08 +0600 Subject: [PATCH 103/290] Fixes parsing of nested Smalltalk blocks --- src/ParsedBytecode.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 9d4593b..8ec1815 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -21,19 +21,19 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { if (instruction.getOpcode() == opcode::pushBlock) { // Preserving the start block's offset - const uint16_t startOffset = decoder.getBytePointer(); + const uint16_t blockStartOffset = decoder.getBytePointer(); // Extra holds the bytecode offset right after the block - const uint16_t stopOffset = instruction.getExtra(); + const uint16_t blockStopOffset = instruction.getExtra(); // Parsing block. This operation depends on // whether we're in a method or in a block. // Nested blocks are registered in the // container method, not the outer block. - std::printf("%.4u : Parsing smalltalk block in interval [%u:%u)\n", currentBytePointer, startOffset, stopOffset); - parseBlock(startOffset, stopOffset); + std::printf("%.4u : Parsing smalltalk block in interval [%u:%u)\n", currentBytePointer, blockStartOffset, blockStopOffset); + parseBlock(blockStartOffset, blockStopOffset); // Skipping the nested block's bytecodes - decoder.setBytePointer(stopOffset); + decoder.setBytePointer(blockStopOffset); continue; } @@ -84,7 +84,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { decoder.setBytePointer(startOffset); while (decoder.getBytePointer() < stopPointer) { const uint16_t currentBytePointer = decoder.getBytePointer(); - if (currentBytePointer) { + if (currentBytePointer != startOffset) { // Switching basic block if current offset is a branch target const TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(currentBytePointer); if (iBlock != m_offsetToBasicBlock.end()) { From f0d6eab79823c0ae55b0a91cfb30b78c7f24bd85 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 6 Feb 2015 11:38:46 +0600 Subject: [PATCH 104/290] Fixes graph constructor to treat SelfReturn as a terminator --- src/ControlGraph.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 4fd6152..33999b5 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -160,6 +160,7 @@ void GraphConstructor::processSpecials(InstructionNode* node) case special::blockReturn: m_currentDomain->requestArgument(0, node); + case special::selfReturn: assert(! m_currentDomain->getTerminator()); m_currentDomain->setTerminator(node); break; From dfeb971e30c6a4ed0248f832c1d158e213707349 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 7 Feb 2015 01:06:14 +0600 Subject: [PATCH 105/290] Fixes GraphConstructor::processPrimitives() Primitive index is stored in the extra part of the instruction. --- src/ControlGraph.cpp | 55 ++++++-------------------------------------- 1 file changed, 7 insertions(+), 48 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 33999b5..7f97533 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -190,59 +190,18 @@ void GraphConstructor::processPrimitives(InstructionNode* node) { const TSmalltalkInstruction& instruction = node->getInstruction(); - switch (instruction.getArgument()) { - case primitive::ioPutChar: - case primitive::getClass: - case primitive::getSize: - case primitive::integerNew: - m_currentDomain->requestArgument(0, node); - break; - - case primitive::objectsAreEqual: - case primitive::startNewProcess: - case primitive::allocateObject: - case primitive::allocateByteArray: - case primitive::cloneByteObject: - - case primitive::arrayAt: - case primitive::stringAt: - - case primitive::smallIntAdd: - case primitive::smallIntDiv: - case primitive::smallIntMod: - case primitive::smallIntLess: - case primitive::smallIntEqual: - case primitive::smallIntMul: - case primitive::smallIntSub: - case primitive::smallIntBitOr: - case primitive::smallIntBitAnd: - case primitive::smallIntBitShift: - - case primitive::LLVMsendMessage: - m_currentDomain->requestArgument(1, node); - m_currentDomain->requestArgument(0, node); - break; - - case primitive::arrayAtPut: - case primitive::stringAtPut: - m_currentDomain->requestArgument(2, node); - m_currentDomain->requestArgument(1, node); - m_currentDomain->requestArgument(0, node); - break; - + switch (instruction.getExtra()) { case primitive::blockInvoke: m_currentDomain->requestArgument(0, node); // block object - for (uint32_t index = instruction.getArgument() - 1; index > 0; index--) // FIXME - m_currentDomain->requestArgument(index - 1, node); // arguments - break; - - case primitive::bulkReplace: - for (uint32_t index = 5; index > 0; ) - m_currentDomain->requestArgument(--index, node); + for (uint32_t index = instruction.getArgument()-1; index > 0; index--) // FIXME + m_currentDomain->requestArgument(index, node); // arguments break; default: - ; //TODO + if (instruction.getArgument() > 0) { + for (int32_t index = instruction.getArgument()-1; index >= 0; index--) + m_currentDomain->requestArgument(index, node); + } } } From d7ff82ed4e9f002a32a7446812abb5b82b2732c1 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 7 Feb 2015 13:58:49 +0600 Subject: [PATCH 106/290] Fixes ParsedBlock registration in ParsedMethod --- src/ParsedBlock.cpp | 4 ++-- src/ParsedMethod.cpp | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/ParsedBlock.cpp b/src/ParsedBlock.cpp index e079357..edc92fc 100644 --- a/src/ParsedBlock.cpp +++ b/src/ParsedBlock.cpp @@ -3,8 +3,8 @@ using namespace st; void ParsedBlock::parseBlock(uint16_t startOffset, uint16_t stopOffset) { - ParsedMethod* container = getContainer(); + ParsedMethod* const container = getContainer(); + ParsedBlock* const nestedBlock = new ParsedBlock(container, startOffset, stopOffset); - ParsedBlock* nestedBlock = new ParsedBlock(container, startOffset, stopOffset); container->addParsedBlock(nestedBlock); } diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index ffda867..562bb8b 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -6,7 +6,7 @@ void ParsedMethod::parseBlock(uint16_t startOffset, uint16_t stopOffset) { // Following instruction belong to the nested code block // ParsedBlock will decode all of it's instructions and nested blocks ParsedBlock* const parsedBlock = new ParsedBlock(this, startOffset, stopOffset); - m_offsetToParsedBlock[startOffset] = parsedBlock; + addParsedBlock(parsedBlock); } void ParsedMethod::addParsedBlock(ParsedBlock* parsedBlock) { @@ -24,9 +24,4 @@ ParsedMethod::~ParsedMethod() { { delete * iBlock; } - for (TOffsetToParsedBlockMap::iterator iBlock = m_offsetToParsedBlock.begin(), - end = m_offsetToParsedBlock.end(); iBlock != end; ++iBlock) - { - delete iBlock->second; - } } From 97d892d4e81c63cf91763485c6c9ab19163acc80 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 7 Feb 2015 15:28:19 +0600 Subject: [PATCH 107/290] Adds logic to skip completely unreachable block during bytecode parsing --- src/ParsedBytecode.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 8ec1815..f294cbc 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -104,6 +104,38 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { } } + // If block does not have referers and do not start at + // offset 0 we treat it as completely unreachable code + if (currentBasicBlock->getReferers().empty() && currentBasicBlock->getOffset() != 0) { + std::printf("%.4u : skipping totally unreachable basic block\n", currentBytePointer); + + // We need to find offset of the next basic block + uint16_t nextOffset = 0; + for (uint16_t offset = currentBytePointer + 1; offset < stopPointer; offset++) { + if (m_offsetToBasicBlock.find(offset) != m_offsetToBasicBlock.end()) { + // Found next basic block's offset + nextOffset = offset; + std::printf("%.4u : found next block at %.4u\n", currentBytePointer, nextOffset); + break; + } + } + + // Erasing block info and stub + m_offsetToBasicBlock.erase(currentBasicBlock->getOffset()); + m_basicBlocks.remove(currentBasicBlock); + delete currentBasicBlock; + + // Switching to the next block + if (nextOffset) { + std::printf("%.4u : switching to the next block at %.4u\n", currentBytePointer, nextOffset); + decoder.setBytePointer(nextOffset); + continue; + } else { + std::printf("%.4u : unreachable block was the last one, nothing to parse here\n", currentBytePointer); + break; + } + } + // Fetching instruction and appending it to the current basic block TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); From 501f5970b41835fd790ae268894a3bbb1c9b8191 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 7 Feb 2015 22:05:48 +0600 Subject: [PATCH 108/290] Adds logic to wipe out chains of unreachable basic blocks --- include/instructions.h | 3 ++ src/ParsedBytecode.cpp | 107 ++++++++++++++++++++++++++++------------- 2 files changed, 77 insertions(+), 33 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index 75e516b..7ce4327 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -207,6 +207,9 @@ class ParsedBytecode { protected: ParsedBytecode(TMethod* method) : m_origin(method) { } void parse(uint16_t startOffset = 0, uint16_t stopOffset = 0); + uint16_t getNextBlockOffset(BasicBlock* currentBlock, uint16_t stopOffset); + void eraseReferer(uint16_t targetOffset, BasicBlock* referer); + void eraseBasicBlock(iterator& iBlock); // Descendants should override this method to provide block handling virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset) = 0; diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index f294cbc..5bb69ba 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -68,6 +68,7 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // If no branch site points to start offset then we create block ourselves if (! currentBasicBlock) { m_offsetToBasicBlock[startOffset] = currentBasicBlock = new BasicBlock(startOffset); +// std::printf("created start basic block %p (%u)\n", currentBasicBlock, startOffset); // Pushing block from the beginning to comply it's position m_basicBlocks.push_front(currentBasicBlock); @@ -104,43 +105,11 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { } } - // If block does not have referers and do not start at - // offset 0 we treat it as completely unreachable code - if (currentBasicBlock->getReferers().empty() && currentBasicBlock->getOffset() != 0) { - std::printf("%.4u : skipping totally unreachable basic block\n", currentBytePointer); - - // We need to find offset of the next basic block - uint16_t nextOffset = 0; - for (uint16_t offset = currentBytePointer + 1; offset < stopPointer; offset++) { - if (m_offsetToBasicBlock.find(offset) != m_offsetToBasicBlock.end()) { - // Found next basic block's offset - nextOffset = offset; - std::printf("%.4u : found next block at %.4u\n", currentBytePointer, nextOffset); - break; - } - } - - // Erasing block info and stub - m_offsetToBasicBlock.erase(currentBasicBlock->getOffset()); - m_basicBlocks.remove(currentBasicBlock); - delete currentBasicBlock; - - // Switching to the next block - if (nextOffset) { - std::printf("%.4u : switching to the next block at %.4u\n", currentBytePointer, nextOffset); - decoder.setBytePointer(nextOffset); - continue; - } else { - std::printf("%.4u : unreachable block was the last one, nothing to parse here\n", currentBytePointer); - break; - } - } - // Fetching instruction and appending it to the current basic block TSmalltalkInstruction instruction = decoder.decodeAndShiftPointer(); // Skipping dead code - if (terminatorEncoded) { + if (terminatorEncoded) { // TODO In case of dead branches erase the target blocks std::printf("%.4u : skipping dead code\n", currentBytePointer); continue; } else { @@ -168,8 +137,78 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { assert(false); } } + + std::printf("Phase 3. Wiping out chains of unreachable blocks\n"); + + // At this stage all possible branches are encoded and block relations are registered in the referer sets. + // We may now iterate through the blocks and wipe out unreachable ones. We need to iterate several times + // because unreachable blocks may form a chain. Stil we may not guarantee, that all unreachable blocks will + // be removed because they may form a cycle where each block refers the other and all have at least 1 referer. + while (true) { + bool blockRemoved = false; + + // Iterating through basic blocks to find blocks that have zero referers and do not start at startOffset + TBasicBlockList::iterator iBlock = m_basicBlocks.begin(); + while (iBlock != m_basicBlocks.end()) { + BasicBlock* const block = *iBlock; + + if (block->getReferers().empty() && block->getOffset() != startOffset) { + std::printf("block %p (%u) is not reachable, erasing and clearing references\n", block, block->getOffset()); + + TSmalltalkInstruction terminator(opcode::extended); + if (block->getTerminator(terminator) && terminator.isBranch()) { + const uint16_t targetOffset = terminator.getExtra(); + const uint16_t skipOffset = (terminator.getArgument() == special::branch) ? 0 : getNextBlockOffset(block, stopOffset); + + eraseReferer(targetOffset, block); + if (skipOffset) + eraseReferer(skipOffset, block); + } + + TBasicBlockList::iterator currentBlock = iBlock++; + eraseBasicBlock(currentBlock); + blockRemoved = true; + continue; + } + + ++iBlock; + } + + // If no block was removed we need to stop + if (!blockRemoved) + break; + } } +void ParsedBytecode::eraseBasicBlock(ParsedBytecode::iterator& iBlock) +{ + BasicBlock* const block = *iBlock; + m_offsetToBasicBlock.erase(block->getOffset()); + m_basicBlocks.erase(iBlock); + delete block; +} + +void ParsedBytecode::eraseReferer(uint16_t targetOffset, BasicBlock* referer) { + const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(targetOffset); + if (iTargetBlock != m_offsetToBasicBlock.end()) { + BasicBlock* const target = iTargetBlock->second; + + std::printf("erasing reference %p (%u) -> %p (%u)\n", referer, referer->getOffset(), target, target->getOffset()); + target->getReferers().erase(referer); + } else { + assert(false); + } +} + +uint16_t ParsedBytecode::getNextBlockOffset(BasicBlock* currentBlock, uint16_t stopOffset) { + for (uint16_t offset = currentBlock->getOffset() + 1; offset < stopOffset; offset++) + if (m_offsetToBasicBlock.find(offset) != m_offsetToBasicBlock.end()) + return offset; + + return 0; +} + + BasicBlock* ParsedBytecode::createBasicBlock(uint16_t blockOffset) { // Checking whether current branch target is already known TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(blockOffset); @@ -182,6 +221,8 @@ BasicBlock* ParsedBytecode::createBasicBlock(uint16_t blockOffset) { m_offsetToBasicBlock[blockOffset] = newBasicBlock; m_basicBlocks.push_back(newBasicBlock); +// std::printf("created new basic block %p (%u)\n", newBasicBlock, newBasicBlock->getOffset()); + return newBasicBlock; } From a30a362e367ad8de67584bfd7a75a4016c687f96 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 8 Feb 2015 13:36:49 +0600 Subject: [PATCH 109/290] Comments out unused variable names to suppress warnings Default visitor actions do not use their parameter. This confuses compiler when warnings are enabled. --- include/analysis.h | 8 ++++---- include/instructions.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 0f5532f..6155f4d 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -150,7 +150,7 @@ class InstructionNode : public ControlNode { const TSmalltalkInstruction& getInstruction() const { return m_instruction; } ControlNode* getArgument(const std::size_t index = 0) const { - assert(index >= 0 && index < m_arguments.size()); + assert(index < m_arguments.size()); return m_arguments[index]; } @@ -408,7 +408,7 @@ class DomainVisitor { DomainVisitor(ControlGraph* graph) : m_graph(graph) { } virtual ~DomainVisitor() { } - virtual bool visitDomain(ControlDomain& domain) { return true; } + virtual bool visitDomain(ControlDomain& /*domain*/) { return true; } virtual void domainsVisited() { } void run() { @@ -434,7 +434,7 @@ class DomainVisitor { class NodeVisitor : public DomainVisitor { public: NodeVisitor(ControlGraph* graph) : DomainVisitor(graph) { } - virtual bool visitNode(ControlNode& node) { return true; } + virtual bool visitNode(ControlNode& /*node*/) { return true; } virtual void nodesVisited() { } protected: @@ -460,7 +460,7 @@ class NodeVisitor : public DomainVisitor { class PlainNodeVisitor { public: PlainNodeVisitor(ControlGraph* graph) : m_graph(graph) { } - virtual bool visitNode(ControlNode& node) { return true; } + virtual bool visitNode(ControlNode& /*node*/) { return true; } virtual void nodesVisited() { } void run() { diff --git a/include/instructions.h b/include/instructions.h index 7ce4327..22b1eb9 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -303,7 +303,7 @@ class BasicBlockVisitor { BasicBlockVisitor(ParsedBytecode* parsedBytecode) : m_parsedBytecode(parsedBytecode) { } virtual ~BasicBlockVisitor() { } - virtual bool visitBlock(BasicBlock& basicBlock) { return true; } + virtual bool visitBlock(BasicBlock& /*basicBlock*/) { return true; } void run() { ParsedBytecode::iterator iBlock = m_parsedBytecode->begin(); @@ -324,7 +324,7 @@ class BasicBlockVisitor { class InstructionVisitor : public BasicBlockVisitor { public: InstructionVisitor(ParsedBytecode* parsedBytecode) : BasicBlockVisitor(parsedBytecode) { } - virtual bool visitInstruction(const TSmalltalkInstruction& instruction) { return true; } + virtual bool visitInstruction(const TSmalltalkInstruction& /*instruction*/) { return true; } protected: virtual bool visitBlock(BasicBlock& basicBlock) { @@ -360,7 +360,7 @@ class ParsedBlockVisitor { } protected: - virtual bool visitBlock(ParsedBlock& parsedBlock) { return true; } + virtual bool visitBlock(ParsedBlock& /*parsedBlock*/) { return true; } private: ParsedMethod* const m_parsedMethod; From a68265d8fd3abf52e5c37df21143e15d6787d744 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 10 Feb 2015 00:17:06 +0600 Subject: [PATCH 110/290] Fixes MethodCompiler's doPrimitive() and doMarkArguments() --- src/MethodCompiler.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index a14893f..49192f2 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -42,6 +42,7 @@ #include #include #include +#include using namespace llvm; @@ -237,6 +238,11 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Creating the function named as "Class>>method" or using provided one jit.function = methodFunction ? methodFunction : createFunction(method); + { + ControlGraphVisualizer vis(jit.controlGraph, std::string(jit.function->getName()) + ".dot"); + vis.run(); + } + // Creating the preamble basic block and inserting it into the function // It will contain basic initialization code (args, temps and so on) jit.preamble = BasicBlock::Create(m_JITModule->getContext(), "preamble", jit.function); @@ -605,6 +611,8 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) Value* argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); Value* argsHolder = protectPointer(jit, argumentsArray); argsHolder->setName("pArgs."); + + jit.currentNode->setValue(argsHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); } @@ -628,7 +636,8 @@ void MethodCompiler::doSendUnary(TJITContext& jit) llvm::Value* MethodCompiler::processLeafNode(st::InstructionNode* instruction) { - + assert(false); + return 0; } llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = 0*/) { @@ -915,7 +924,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::popTop: // This should be completely eliminated by graph constructor - assert(false); +// assert(false); // if (jit.hasValue()) // jit.popValue(0, true); break; @@ -991,7 +1000,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) void MethodCompiler::doPrimitive(TJITContext& jit) { - uint32_t opcode = jit.currentNode->getInstruction().getArgument(); + uint32_t opcode = jit.currentNode->getInstruction().getExtra(); Value* primitiveResult = 0; Value* primitiveFailed = jit.builder->getFalse(); From 1622434426de78f1a6b401c201d620c59ce5caf2 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 10 Feb 2015 10:37:43 +0600 Subject: [PATCH 111/290] Fixes MethodCompiler::scanForBranches() --- include/jit.h | 2 +- src/MethodCompiler.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/jit.h b/include/jit.h index f4c0d73..7552d47 100644 --- a/include/jit.h +++ b/include/jit.h @@ -233,7 +233,7 @@ class MethodCompiler { private: llvm::Module* m_JITModule; std::map m_targetToBlockMap; - void scanForBranches(TJITContext& jit, uint32_t byteCount = 0); + void scanForBranches(TJITContext& jit, st::ParsedBytecode* source, uint32_t byteCount = 0); bool scanForBlockReturn(TJITContext& jit, uint32_t byteCount = 0); std::map m_blockFunctions; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 49192f2..f3a2a6d 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -181,14 +181,14 @@ bool MethodCompiler::scanForBlockReturn(TJITContext& jit, uint32_t byteCount/* = return detector.isBlockReturnFound(); } -void MethodCompiler::scanForBranches(TJITContext& jit, uint32_t byteCount /*= 0*/) +void MethodCompiler::scanForBranches(TJITContext& jit, st::ParsedBytecode* source, uint32_t byteCount /*= 0*/) { // Iterating over method's basic blocks and creating their representation in LLVM // Created blocks are collected in the m_targetToBlockMap map with bytecode offset as a key class Visitor : public st::BasicBlockVisitor { public: - Visitor(TJITContext& jit) : BasicBlockVisitor(jit.parsedMethod), m_jit(jit) { } + Visitor(TJITContext& jit, st::ParsedBytecode* source) : BasicBlockVisitor(source), m_jit(jit) { } private: virtual bool visitBlock(st::BasicBlock& basicBlock) { @@ -206,7 +206,7 @@ void MethodCompiler::scanForBranches(TJITContext& jit, uint32_t byteCount /*= 0* TJITContext& m_jit; }; - Visitor visitor(jit); + Visitor visitor(jit, source); visitor.run(); } @@ -270,7 +270,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // collects branch targets. Creates target basic blocks beforehand. // Target blocks are collected in the m_targetToBlockMap map with // target bytecode offset as a key. - scanForBranches(jit); + scanForBranches(jit, jit.parsedMethod); // Switching builder context to the first basic block from the preamble BasicBlock* body = m_targetToBlockMap[0]; @@ -538,7 +538,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) blockContext.preamble = BasicBlock::Create(m_JITModule->getContext(), "blockPreamble", blockContext.function); blockContext.builder = new IRBuilder<>(blockContext.preamble); writePreamble(blockContext, /*isBlock*/ true); - scanForBranches(blockContext); + scanForBranches(blockContext, blockContext.parsedBlock); BasicBlock* blockBody = BasicBlock::Create(m_JITModule->getContext(), "blockBody", blockContext.function); blockContext.builder->CreateBr(blockBody); @@ -547,7 +547,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) writeFunctionBody(blockContext); // Running optimization passes on a block function - JITRuntime::Instance()->optimizeFunction(blockContext.function); + //JITRuntime::Instance()->optimizeFunction(blockContext.function); } // Create block object and fill it with context information From 200c495f4a794392c39b3cd51ebf4955554ac98d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 10 Feb 2015 23:20:58 +0600 Subject: [PATCH 112/290] Fixes branching and arguments --- include/jit.h | 2 ++ src/JITRuntime.cpp | 2 ++ src/MethodCompiler.cpp | 49 +++++++++++++++++++++++++++++++++++------- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/include/jit.h b/include/jit.h index 7552d47..24073ce 100644 --- a/include/jit.h +++ b/include/jit.h @@ -288,6 +288,8 @@ class MethodCompiler { llvm::Value* createArray(TJITContext& jit, uint32_t elementsCount); llvm::Function* createFunction(TMethod* method); + uint16_t getSkipOffset(st::InstructionNode* branch); + uint32_t m_callSiteIndex; std::map m_callSiteIndexToOffset; public: diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index 0c97fda..2a7f005 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -309,6 +309,8 @@ TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext) std::exit(1); } + outs() << *blockFunction; + verifyModule(*m_JITModule, AbortProcessAction); optimizeFunction(blockFunction); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index f3a2a6d..a51eec2 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -192,10 +192,13 @@ void MethodCompiler::scanForBranches(TJITContext& jit, st::ParsedBytecode* sourc private: virtual bool visitBlock(st::BasicBlock& basicBlock) { + std::ostringstream offset; + offset << "offset" << basicBlock.getOffset(); + MethodCompiler* compiler = m_jit.compiler; // self reference llvm::BasicBlock* newBlock = llvm::BasicBlock::Create( compiler->m_JITModule->getContext(), // creating context - "branch.", // new block's name + offset.str(), // new block's name m_jit.function // method's function ); @@ -274,7 +277,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Switching builder context to the first basic block from the preamble BasicBlock* body = m_targetToBlockMap[0]; - body->setName("body."); + body->setName("offset0"); jit.builder->SetInsertPoint(jit.preamble); jit.builder->CreateBr(body); @@ -285,6 +288,8 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Processing the method's bytecodes writeFunctionBody(jit); + outs() << *jit.function; + // Cleaning up m_blockFunctions.clear(); m_targetToBlockMap.clear(); @@ -540,7 +545,11 @@ void MethodCompiler::doPushBlock(TJITContext& jit) writePreamble(blockContext, /*isBlock*/ true); scanForBranches(blockContext, blockContext.parsedBlock); - BasicBlock* blockBody = BasicBlock::Create(m_JITModule->getContext(), "blockBody", blockContext.function); + ss.str(""); + ss << "offset" << blockOffset; + BasicBlock* blockBody = m_targetToBlockMap[blockOffset]; + blockBody->setName(ss.str()); + blockContext.builder->CreateBr(blockBody); blockContext.builder->SetInsertPoint(blockBody); @@ -568,7 +577,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) void MethodCompiler::doAssignTemporary(TJITContext& jit) { uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* value = jit.currentNode->getArgument()->getValue(); //jit.lastValue(); + Value* value = getArgument(jit); //jit.lastValue(); IRBuilder<>& builder = * jit.builder; Function* getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); @@ -580,7 +589,7 @@ void MethodCompiler::doAssignTemporary(TJITContext& jit) void MethodCompiler::doAssignInstance(TJITContext& jit) { uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* value = jit.currentNode->getArgument()->getValue(); // jit.lastValue(); + Value* value = getArgument(jit); // jit.lastValue(); IRBuilder<>& builder = * jit.builder; Value* self = jit.getSelf(); @@ -944,16 +953,19 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::branchIfTrue: case special::branchIfFalse: { // Loading branch target bytecode offset - uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); + const uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); + const uint32_t skipOffset = getSkipOffset(jit.currentNode); if (!iPreviousInst->isTerminator()) { + jit.currentNode->getOutEdges(); + // Finding appropriate branch target // from the previously stored basic blocks BasicBlock* targetBlock = m_targetToBlockMap[targetOffset]; // This is a block that goes right after the branch instruction. // If branch condition is not met execution continues right after - BasicBlock* skipBlock = BasicBlock::Create(m_JITModule->getContext(), "branchSkip.", jit.function); + BasicBlock* skipBlock = m_targetToBlockMap[skipOffset]; // Creating condition check Value* boolObject = (opcode == special::branchIfTrue) ? m_globals.trueObject : m_globals.falseObject; @@ -962,7 +974,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) jit.builder->CreateCondBr(boolValue, targetBlock, skipBlock); // Switching to a newly created block - jit.builder->SetInsertPoint(skipBlock); + //jit.builder->SetInsertPoint(skipBlock); } } break; @@ -998,6 +1010,27 @@ void MethodCompiler::doSpecial(TJITContext& jit) } } +uint16_t MethodCompiler::getSkipOffset(st::InstructionNode* branch) +{ + assert(branch->getInstruction().isBranch()); + assert(branch->getInstruction().opcode != special::branch); + assert(branch->getOutEdges().size() == 2); + + // One of the offsets we know. It is the target offset when condition is met. + const uint16_t targetOffset = branch->getInstruction().getExtra(); + + // Other one will be offset of the block to which points the other edge + st::TNodeSet::iterator iNode = branch->getOutEdges().begin(); + const st::ControlNode* const edge1 = *iNode++; + const st::ControlNode* const edge2 = *iNode; + + const uint16_t offset = edge1->getDomain()->getBasicBlock()->getOffset(); + if (offset == targetOffset) + return edge2->getDomain()->getBasicBlock()->getOffset(); + else + return offset; +} + void MethodCompiler::doPrimitive(TJITContext& jit) { uint32_t opcode = jit.currentNode->getInstruction().getExtra(); From cc5eb50bd0a8ec1d3b51dfc6933c4ead92dabf85 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 10 Feb 2015 23:33:51 +0600 Subject: [PATCH 113/290] Fixes value storing --- src/JITRuntime.cpp | 2 +- src/MethodCompiler.cpp | 32 ++++++-------------------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index 2a7f005..cebb5be 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -309,7 +309,7 @@ TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext) std::exit(1); } - outs() << *blockFunction; + outs() << *blockFunction << "\n"; verifyModule(*m_JITModule, AbortProcessAction); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index a51eec2..07da699 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -621,8 +621,8 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) Value* argsHolder = protectPointer(jit, argumentsArray); argsHolder->setName("pArgs."); - jit.currentNode->setValue(argsHolder); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); + setNodeValue(jit.currentNode, argsHolder); + // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); } void MethodCompiler::doSendUnary(TJITContext& jit) @@ -912,30 +912,12 @@ void MethodCompiler::doSpecial(TJITContext& jit) break; case special::duplicate: - // FIXME Duplicate the TStackValue, not the result - { - // We're popping the value from the stack to a temporary holder - // and then pushing two lazy stack values pointing to it. - - Value* dupValue = getArgument(jit); // jit.popValue(); - Value* dupHolder = protectPointer(jit, dupValue); - dupHolder->setName("pDup."); - - // Two equal values are pushed on the stack - jit.currentNode->setValue(dupHolder); - - // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, dupHolder)); - // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, dupHolder)); - } - - //jit.pushValue(jit.lastValue()); + // This should be completely eliminated by graph constructor + assert(false); break; case special::popTop: - // This should be completely eliminated by graph constructor -// assert(false); -// if (jit.hasValue()) -// jit.popValue(0, true); + // Simply doing nothing break; case special::branch: { @@ -957,8 +939,6 @@ void MethodCompiler::doSpecial(TJITContext& jit) const uint32_t skipOffset = getSkipOffset(jit.currentNode); if (!iPreviousInst->isTerminator()) { - jit.currentNode->getOutEdges(); - // Finding appropriate branch target // from the previously stored basic blocks BasicBlock* targetBlock = m_targetToBlockMap[targetOffset]; @@ -1053,7 +1033,6 @@ void MethodCompiler::doPrimitive(TJITContext& jit) BasicBlock* primitiveFailedBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveFailedBB", jit.function); compilePrimitive(jit, opcode, primitiveResult, primitiveFailed, primitiveSucceededBB, primitiveFailedBB); - jit.currentNode->setValue(primitiveResult); jit.builder->CreateCondBr(primitiveFailed, primitiveFailedBB, primitiveSucceededBB); jit.builder->SetInsertPoint(primitiveSucceededBB); @@ -1061,6 +1040,7 @@ void MethodCompiler::doPrimitive(TJITContext& jit) jit.builder->CreateRet(primitiveResult); jit.builder->SetInsertPoint(primitiveFailedBB); + setNodeValue(jit.currentNode, primitiveResult); // jit.pushValue(m_globals.nilObject); } From b34f42720627bd4522225f3de303f33e878387fd Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 11 Feb 2015 10:49:23 +0600 Subject: [PATCH 114/290] Promotes const to values and removes obsolete logic --- src/MethodCompiler.cpp | 458 +++++++++++++++++++++-------------------- 1 file changed, 231 insertions(+), 227 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 07da699..098dc23 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -576,26 +576,26 @@ void MethodCompiler::doPushBlock(TJITContext& jit) void MethodCompiler::doAssignTemporary(TJITContext& jit) { - uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* value = getArgument(jit); //jit.lastValue(); + const uint8_t index = jit.currentNode->getInstruction().getArgument(); + Value* const value = getArgument(jit); //jit.lastValue(); IRBuilder<>& builder = * jit.builder; Function* getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); - Value* context = jit.getCurrentContext(); - Value* temps = builder.CreateCall(getTempsFromContext, context); + Value* const context = jit.getCurrentContext(); + Value* const temps = builder.CreateCall(getTempsFromContext, context); builder.CreateCall3(m_baseFunctions.setObjectField, temps, builder.getInt32(index), value); } void MethodCompiler::doAssignInstance(TJITContext& jit) { - uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* value = getArgument(jit); // jit.lastValue(); + const uint8_t index = jit.currentNode->getInstruction().getArgument(); + Value* const value = getArgument(jit); // jit.lastValue(); IRBuilder<>& builder = * jit.builder; - Value* self = jit.getSelf(); + Function* const getObjectFieldPtr = m_JITModule->getFunction("getObjectFieldPtr"); + Value* const self = jit.getSelf(); + Value* const fieldPointer = builder.CreateCall2(getObjectFieldPtr, self, builder.getInt32(index)); - Function* getObjectFieldPtr = m_JITModule->getFunction("getObjectFieldPtr"); - Value* fieldPointer = builder.CreateCall2(getObjectFieldPtr, self, builder.getInt32(index)); builder.CreateCall2(m_runtimeAPI.checkRoot, value, fieldPointer); builder.CreateStore(value, fieldPointer); } @@ -603,22 +603,22 @@ void MethodCompiler::doAssignInstance(TJITContext& jit) void MethodCompiler::doMarkArguments(TJITContext& jit) { // Here we need to create the arguments array from the values on the stack - uint8_t argumentsCount = jit.currentNode->getInstruction().getArgument(); + const uint8_t argumentsCount = jit.currentNode->getInstruction().getArgument(); // FIXME Probably we may unroll the arguments array and pass the values directly. // However, in some cases this may lead to additional architectural problems. - Value* argumentsObject = createArray(jit, argumentsCount); + Value* const argumentsObject = createArray(jit, argumentsCount); // Filling object with contents uint8_t index = argumentsCount; assert(argumentsCount == jit.currentNode->getArgumentsCount()); while (index > 0) { - Value* value = getArgument(jit, index - 1); // jit.popValue(); - jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), value); + Value* const argument = getArgument(jit, index - 1); // jit.popValue(); + jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), argument); } - Value* argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); - Value* argsHolder = protectPointer(jit, argumentsArray); + Value* const argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); + Value* const argsHolder = protectPointer(jit, argumentsArray); argsHolder->setName("pArgs."); setNodeValue(jit.currentNode, argsHolder); @@ -627,9 +627,9 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) void MethodCompiler::doSendUnary(TJITContext& jit) { - Value* value = getArgument(jit); // jit.popValue(); - Value* condition = 0; + Value* const value = getArgument(jit); // jit.popValue(); + Value* condition = 0; switch ( static_cast(jit.currentNode->getInstruction().getArgument()) ) { case unaryBuiltIns::isNil: condition = jit.builder->CreateICmpEQ(value, m_globals.nilObject, "isNil."); break; case unaryBuiltIns::notNil: condition = jit.builder->CreateICmpNE(value, m_globals.nilObject, "notNil."); break; @@ -638,7 +638,8 @@ void MethodCompiler::doSendUnary(TJITContext& jit) std::fprintf(stderr, "JIT: Invalid opcode %d passed to sendUnary\n", jit.currentNode->getInstruction().getArgument()); } - Value* result = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); + // FIXME Do not protect the result object because it will always be the literal value + Value* const result = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); setNodeValue(jit.currentNode, result); //jit.pushValue(result); } @@ -666,7 +667,7 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node) } const std::size_t inEdgesCount = node->getInEdges().size(); - if (st::InstructionNode* instruction = node->cast()) { + if (st::InstructionNode* const instruction = node->cast()) { // If node is a leaf from the same domain we may encode it locally. // If not, creating a dummy value wich will be replaced by real value later. @@ -706,7 +707,7 @@ void MethodCompiler::setNodeValue(st::ControlNode* node, llvm::Value* value) { assert(value); - Value* oldValue = node->getValue(); + Value* const oldValue = node->getValue(); if (oldValue) { if (isa(oldValue)) oldValue->replaceAllUsesWith(value); @@ -722,17 +723,17 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // 0, 1 or 2 for '<', '<=' or '+' respectively binaryBuiltIns::Operator opcode = static_cast(jit.currentNode->getInstruction().getArgument()); - Value* rightValue = getArgument(jit, 1); // jit.popValue(); - Value* leftValue = getArgument(jit, 0); // jit.popValue(); + Value* const rightValue = getArgument(jit, 1); // jit.popValue(); + Value* const leftValue = getArgument(jit, 0); // jit.popValue(); // Checking if values are both small integers - Value* rightIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, rightValue); - Value* leftIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, leftValue); - Value* isSmallInts = jit.builder->CreateAnd(rightIsInt, leftIsInt); + Value* const rightIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, rightValue); + Value* const leftIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, leftValue); + Value* const isSmallInts = jit.builder->CreateAnd(rightIsInt, leftIsInt); - BasicBlock* integersBlock = BasicBlock::Create(m_JITModule->getContext(), "asIntegers.", jit.function); - BasicBlock* sendBinaryBlock = BasicBlock::Create(m_JITModule->getContext(), "asObjects.", jit.function); - BasicBlock* resultBlock = BasicBlock::Create(m_JITModule->getContext(), "result.", jit.function); + BasicBlock* const integersBlock = BasicBlock::Create(m_JITModule->getContext(), "asIntegers.", jit.function); + BasicBlock* const sendBinaryBlock = BasicBlock::Create(m_JITModule->getContext(), "asObjects.", jit.function); + BasicBlock* const resultBlock = BasicBlock::Create(m_JITModule->getContext(), "result.", jit.function); // Depending on the contents we may either do the integer operations // directly or create a send message call using operand objects @@ -740,15 +741,15 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // Now the integers part jit.builder->SetInsertPoint(integersBlock); - Value* rightInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, rightValue); - Value* leftInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, leftValue); + Value* const rightInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, rightValue); + Value* const leftInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, leftValue); Value* intResult = 0; // this will be an immediate operation result Value* intResultObject = 0; // this will be actual object to return switch (opcode) { - case binaryBuiltIns::operatorLess : intResult = jit.builder->CreateICmpSLT(leftInt, rightInt); break; + case binaryBuiltIns::operatorLess: intResult = jit.builder->CreateICmpSLT(leftInt, rightInt); break; case binaryBuiltIns::operatorLessOrEq: intResult = jit.builder->CreateICmpSLE(leftInt, rightInt); break; - case binaryBuiltIns::operatorPlus : intResult = jit.builder->CreateAdd(leftInt, rightInt); break; + case binaryBuiltIns::operatorPlus: intResult = jit.builder->CreateAdd(leftInt, rightInt); break; default: std::fprintf(stderr, "JIT: Invalid opcode %d passed to sendBinary\n", opcode); } @@ -760,7 +761,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // We need to create TInteger value and cast it to the pointer // Interpreting raw integer value as a pointer - Value* smalltalkInt = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult, "intAsPtr."); + Value* const smalltalkInt = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult, "intAsPtr."); intResultObject = jit.builder->CreateIntToPtr(smalltalkInt, m_baseTypes.object->getPointerTo()); intResultObject->setName("sum."); } else { @@ -821,29 +822,29 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // We do not know now which way the program will be executed, // so we need to aggregate two possible results one of which // will be then selected as a return value - PHINode* phi = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), 2, "phi."); + PHINode* const phi = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), 2, "phi."); phi->addIncoming(intResultObject, integersBlock); phi->addIncoming(sendMessageResult, sendBinaryBlock); - Value* resultHolder = protectPointer(jit, phi); + Value* const resultHolder = protectPointer(jit, phi); setNodeValue(jit.currentNode, resultHolder); //jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } void MethodCompiler::doSendMessage(TJITContext& jit) { - Value* arguments = getArgument(jit); // jit.popValue(); + Value* const arguments = getArgument(jit); // jit.popValue(); // First of all we need to get the actual message selector - Value* selectorObject = jit.getLiteral(jit.currentNode->getInstruction().getArgument()); - Value* messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); + Value* const selectorObject = jit.getLiteral(jit.currentNode->getInstruction().getArgument()); + Value* const messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); std::ostringstream ss; ss << "#" << jit.originMethod->literals->getField(jit.currentNode->getInstruction().getArgument())->toString() << "."; messageSelector->setName(ss.str()); // Forming a message parameters - Value* sendMessageArgs[] = { + Value* const sendMessageArgs[] = { jit.getCurrentContext(), // calling context messageSelector, // selector arguments, // message arguments @@ -857,19 +858,19 @@ void MethodCompiler::doSendMessage(TJITContext& jit) Value* result = 0; if (jit.methodHasBlockReturn) { // Creating basic block that will be branched to on normal invoke - BasicBlock* nextBlock = BasicBlock::Create(m_JITModule->getContext(), "next.", jit.function); + BasicBlock* const nextBlock = BasicBlock::Create(m_JITModule->getContext(), "next.", jit.function); // Performing a function invoke result = jit.builder->CreateInvoke(m_runtimeAPI.sendMessage, nextBlock, jit.exceptionLandingPad, sendMessageArgs); - // Switching builder to new block + // Switching builder to a new block jit.builder->SetInsertPoint(nextBlock); } else { // Just calling the function. No block switching is required result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); } - Value* resultHolder = protectPointer(jit, result); + Value* const resultHolder = protectPointer(jit, result); setNodeValue(jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } @@ -878,30 +879,30 @@ void MethodCompiler::doSpecial(TJITContext& jit) { const uint8_t opcode = jit.currentNode->getInstruction().getArgument(); - BasicBlock::iterator iPreviousInst = jit.builder->GetInsertPoint(); - if (iPreviousInst != jit.builder->GetInsertBlock()->begin()) - --iPreviousInst; +// BasicBlock::iterator iPreviousInst = jit.builder->GetInsertPoint(); +// if (iPreviousInst != jit.builder->GetInsertBlock()->begin()) +// --iPreviousInst; switch (opcode) { case special::selfReturn: - if (! iPreviousInst->isTerminator()) - jit.builder->CreateRet(jit.getSelf()); +// if (! iPreviousInst->isTerminator()) + jit.builder->CreateRet(jit.getSelf()); break; case special::stackReturn: // if ( !iPreviousInst->isTerminator() && jit.hasValue() ) - jit.builder->CreateRet(getArgument(jit)); // jit.popValue()); + jit.builder->CreateRet(getArgument(jit)); // jit.popValue()); break; case special::blockReturn: /*if ( !iPreviousInst->isTerminator() && jit.hasValue())*/ { // Peeking the return value from the stack - Value* value = getArgument(jit); // jit.popValue(); + Value* const value = getArgument(jit); // jit.popValue(); // Loading the target context information - Value* blockContext = jit.builder->CreateBitCast(jit.getCurrentContext(), m_baseTypes.block->getPointerTo()); - Value* creatingContextPtr = jit.builder->CreateStructGEP(blockContext, 2); - Value* targetContext = jit.builder->CreateLoad(creatingContextPtr); + Value* const blockContext = jit.builder->CreateBitCast(jit.getCurrentContext(), m_baseTypes.block->getPointerTo()); + Value* const creatingContextPtr = jit.builder->CreateStructGEP(blockContext, 2); + Value* const targetContext = jit.builder->CreateLoad(creatingContextPtr); // Emitting the TBlockReturn exception jit.builder->CreateCall2(m_runtimeAPI.emitBlockReturn, value, targetContext); @@ -922,12 +923,13 @@ void MethodCompiler::doSpecial(TJITContext& jit) case special::branch: { // Loading branch target bytecode offset - uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); + const uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); - if (!iPreviousInst->isTerminator()) { +// if (!iPreviousInst->isTerminator()) + { // Finding appropriate branch target // from the previously stored basic blocks - BasicBlock* target = m_targetToBlockMap[targetOffset]; + BasicBlock* const target = m_targetToBlockMap[targetOffset]; jit.builder->CreateBr(target); } } break; @@ -938,19 +940,20 @@ void MethodCompiler::doSpecial(TJITContext& jit) const uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); const uint32_t skipOffset = getSkipOffset(jit.currentNode); - if (!iPreviousInst->isTerminator()) { +// if (!iPreviousInst->isTerminator()) + { // Finding appropriate branch target // from the previously stored basic blocks - BasicBlock* targetBlock = m_targetToBlockMap[targetOffset]; + BasicBlock* const targetBlock = m_targetToBlockMap[targetOffset]; // This is a block that goes right after the branch instruction. // If branch condition is not met execution continues right after - BasicBlock* skipBlock = m_targetToBlockMap[skipOffset]; + BasicBlock* const skipBlock = m_targetToBlockMap[skipOffset]; // Creating condition check - Value* boolObject = (opcode == special::branchIfTrue) ? m_globals.trueObject : m_globals.falseObject; - Value* condition = getArgument(jit); // jit.popValue(); - Value* boolValue = jit.builder->CreateICmpEQ(condition, boolObject); + Value* const boolObject = (opcode == special::branchIfTrue) ? m_globals.trueObject : m_globals.falseObject; + Value* const condition = getArgument(jit); // jit.popValue(); + Value* const boolValue = jit.builder->CreateICmpEQ(condition, boolObject); jit.builder->CreateCondBr(boolValue, targetBlock, skipBlock); // Switching to a newly created block @@ -959,18 +962,18 @@ void MethodCompiler::doSpecial(TJITContext& jit) } break; case special::sendToSuper: { - Value* argsObject = getArgument(jit); // jit.popValue(); - Value* arguments = jit.builder->CreateBitCast(argsObject, m_baseTypes.objectArray->getPointerTo()); + Value* const argsObject = getArgument(jit); // jit.popValue(); + Value* const arguments = jit.builder->CreateBitCast(argsObject, m_baseTypes.objectArray->getPointerTo()); - uint32_t literalIndex = jit.currentNode->getInstruction().getExtra(); - Value* selectorObject = jit.getLiteral(literalIndex); - Value* messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); + const uint32_t literalIndex = jit.currentNode->getInstruction().getExtra(); + Value* const selectorObject = jit.getLiteral(literalIndex); + Value* const messageSelector = jit.builder->CreateBitCast(selectorObject, m_baseTypes.symbol->getPointerTo()); - Value* currentClass = jit.getMethodClass(); - Value* parentClassPtr = jit.builder->CreateStructGEP(currentClass, 2); - Value* parentClass = jit.builder->CreateLoad(parentClassPtr); + Value* const currentClass = jit.getMethodClass(); + Value* const parentClassPtr = jit.builder->CreateStructGEP(currentClass, 2); + Value* const parentClass = jit.builder->CreateLoad(parentClassPtr); - Value* sendMessageArgs[] = { + Value* const sendMessageArgs[] = { jit.getCurrentContext(), // calling context messageSelector, // selector arguments, // message arguments @@ -979,8 +982,8 @@ void MethodCompiler::doSpecial(TJITContext& jit) }; m_callSiteIndexToOffset[m_callSiteIndex++] = jit.currentNode->getIndex(); - Value* result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); - Value* resultHolder = protectPointer(jit, result); + Value* const result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); + Value* const resultHolder = protectPointer(jit, result); setNodeValue(jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } break; @@ -1029,8 +1032,8 @@ void MethodCompiler::doPrimitive(TJITContext& jit) // If your primitive may fail, you may use 2 ways: // 1) set br primitiveFailedBB // 2) bind primitiveFailed with any i1 result - BasicBlock* primitiveSucceededBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveSucceededBB", jit.function); - BasicBlock* primitiveFailedBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveFailedBB", jit.function); + BasicBlock* const primitiveSucceededBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveSucceededBB", jit.function); + BasicBlock* const primitiveFailedBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveFailedBB", jit.function); compilePrimitive(jit, opcode, primitiveResult, primitiveFailed, primitiveSucceededBB, primitiveFailedBB); @@ -1054,102 +1057,103 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, { switch (opcode) { case primitive::objectsAreEqual: { - Value* object2 = getArgument(jit, 1); // jit.popValue(); - Value* object1 = getArgument(jit, 0); // jit.popValue(); + Value* const object2 = getArgument(jit, 1); // jit.popValue(); + Value* const object1 = getArgument(jit, 0); // jit.popValue(); - Value* result = jit.builder->CreateICmpEQ(object1, object2); - Value* boolValue = jit.builder->CreateSelect(result, m_globals.trueObject, m_globals.falseObject); + Value* const result = jit.builder->CreateICmpEQ(object1, object2); + Value* const boolValue = jit.builder->CreateSelect(result, m_globals.trueObject, m_globals.falseObject); primitiveResult = boolValue; } break; // TODO ioGetchar case primitive::ioPutChar: { - Value* intObject = getArgument(jit); // jit.popValue(); - Value* intValue = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, intObject); - Value* charValue = jit.builder->CreateTrunc(intValue, jit.builder->getInt8Ty()); + Value* const intObject = getArgument(jit); // jit.popValue(); + Value* const intValue = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, intObject); + Value* const charValue = jit.builder->CreateTrunc(intValue, jit.builder->getInt8Ty()); - Function* putcharFunc = cast(m_JITModule->getOrInsertFunction("putchar", jit.builder->getInt32Ty(), jit.builder->getInt8Ty(), NULL)); + Function* const putcharFunc = cast(m_JITModule->getOrInsertFunction("putchar", jit.builder->getInt32Ty(), jit.builder->getInt8Ty(), NULL)); jit.builder->CreateCall(putcharFunc, charValue); primitiveResult = m_globals.nilObject; } break; case primitive::getClass: { - Value* object = getArgument(jit); // jit.popValue(); - Value* klass = jit.builder->CreateCall(m_baseFunctions.getObjectClass, object, "class"); + Value* const object = getArgument(jit); // jit.popValue(); + Value* const klass = jit.builder->CreateCall(m_baseFunctions.getObjectClass, object, "class"); primitiveResult = jit.builder->CreateBitCast(klass, m_baseTypes.object->getPointerTo()); } break; + case primitive::getSize: { - Value* object = getArgument(jit); // jit.popValue(); - Value* objectIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, object, "isSmallInt"); + Value* const object = getArgument(jit); // jit.popValue(); + Value* const objectIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, object, "isSmallInt"); - BasicBlock* asSmallInt = BasicBlock::Create(m_JITModule->getContext(), "asSmallInt", jit.function); - BasicBlock* asObject = BasicBlock::Create(m_JITModule->getContext(), "asObject", jit.function); + BasicBlock* const asSmallInt = BasicBlock::Create(m_JITModule->getContext(), "asSmallInt", jit.function); + BasicBlock* const asObject = BasicBlock::Create(m_JITModule->getContext(), "asObject", jit.function); jit.builder->CreateCondBr(objectIsSmallInt, asSmallInt, asObject); jit.builder->SetInsertPoint(asSmallInt); - Value* result = jit.builder->CreateCall(m_baseFunctions.newInteger, jit.builder->getInt32(0)); + Value* const result = jit.builder->CreateCall(m_baseFunctions.newInteger, jit.builder->getInt32(0)); jit.builder->CreateRet(result); jit.builder->SetInsertPoint(asObject); - Value* size = jit.builder->CreateCall(m_baseFunctions.getObjectSize, object, "size"); - primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, size); + Value* const size = jit.builder->CreateCall(m_baseFunctions.getObjectSize, object, "size"); + primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, size); } break; case primitive::startNewProcess: { // 6 // /* ticks. unused */ jit.popValue(); - Value* processObject = getArgument(jit, 1); // jit.popValue(); - Value* process = jit.builder->CreateBitCast(processObject, m_baseTypes.process->getPointerTo()); + Value* const processObject = getArgument(jit, 1); // jit.popValue(); + Value* const process = jit.builder->CreateBitCast(processObject, m_baseTypes.process->getPointerTo()); - Function* executeProcess = m_JITModule->getFunction("executeProcess"); - Value* processResult = jit.builder->CreateCall(executeProcess, process); + Function* const executeProcess = m_JITModule->getFunction("executeProcess"); + Value* const processResult = jit.builder->CreateCall(executeProcess, process); primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, processResult); } break; case primitive::allocateObject: { // 7 - Value* sizeObject = getArgument(jit, 1); // jit.popValue(); - Value* klassObject = getArgument(jit, 0); // jit.popValue(); - Value* klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); + Value* const sizeObject = getArgument(jit, 1); // jit.popValue(); + Value* const klassObject = getArgument(jit, 0); // jit.popValue(); + Value* const klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); - Value* size = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, sizeObject, "size."); - Value* slotSize = jit.builder->CreateCall(m_baseFunctions.getSlotSize, size, "slotSize."); - Value* newInstance = jit.builder->CreateCall2(m_runtimeAPI.newOrdinaryObject, klass, slotSize, "instance."); + Value* const size = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, sizeObject, "size."); + Value* const slotSize = jit.builder->CreateCall(m_baseFunctions.getSlotSize, size, "slotSize."); + Value* const newInstance = jit.builder->CreateCall2(m_runtimeAPI.newOrdinaryObject, klass, slotSize, "instance."); primitiveResult = newInstance; } break; case primitive::allocateByteArray: { // 20 - Value* sizeObject = getArgument(jit, 1); // jit.popValue(); - Value* klassObject = getArgument(jit, 0); // jit.popValue(); + Value* const sizeObject = getArgument(jit, 1); // jit.popValue(); + Value* const klassObject = getArgument(jit, 0); // jit.popValue(); - Value* klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); - Value* dataSize = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, sizeObject, "dataSize."); - Value* newInstance = jit.builder->CreateCall2(m_runtimeAPI.newBinaryObject, klass, dataSize, "instance."); + Value* const klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); + Value* const dataSize = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, sizeObject, "dataSize."); + Value* const newInstance = jit.builder->CreateCall2(m_runtimeAPI.newBinaryObject, klass, dataSize, "instance."); primitiveResult = jit.builder->CreateBitCast(newInstance, m_baseTypes.object->getPointerTo() ); } break; case primitive::cloneByteObject: { // 23 - Value* klassObject = getArgument(jit, 1); // jit.popValue(); - Value* original = getArgument(jit, 0); // jit.popValue(); - Value* originalHolder = protectPointer(jit, original); + Value* const klassObject = getArgument(jit, 1); // jit.popValue(); + Value* const original = getArgument(jit, 0); // jit.popValue(); + Value* const originalHolder = protectPointer(jit, original); - Value* klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); - Value* dataSize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, original, "dataSize."); - Value* clone = jit.builder->CreateCall2(m_runtimeAPI.newBinaryObject, klass, dataSize, "clone."); + Value* const klass = jit.builder->CreateBitCast(klassObject, m_baseTypes.klass->getPointerTo()); + Value* const dataSize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, original, "dataSize."); + Value* const clone = jit.builder->CreateCall2(m_runtimeAPI.newBinaryObject, klass, dataSize, "clone."); - Value* originalObject = jit.builder->CreateBitCast(jit.builder->CreateLoad(originalHolder), m_baseTypes.object->getPointerTo()); - Value* cloneObject = jit.builder->CreateBitCast(clone, m_baseTypes.object->getPointerTo()); - Value* sourceFields = jit.builder->CreateCall(m_baseFunctions.getObjectFields, originalObject); - Value* destFields = jit.builder->CreateCall(m_baseFunctions.getObjectFields, cloneObject); + Value* const originalObject = jit.builder->CreateBitCast(jit.builder->CreateLoad(originalHolder), m_baseTypes.object->getPointerTo()); + Value* const cloneObject = jit.builder->CreateBitCast(clone, m_baseTypes.object->getPointerTo()); + Value* const sourceFields = jit.builder->CreateCall(m_baseFunctions.getObjectFields, originalObject); + Value* const destFields = jit.builder->CreateCall(m_baseFunctions.getObjectFields, cloneObject); - Value* source = jit.builder->CreateBitCast(sourceFields, jit.builder->getInt8PtrTy()); - Value* destination = jit.builder->CreateBitCast(destFields, jit.builder->getInt8PtrTy()); + Value* const source = jit.builder->CreateBitCast(sourceFields, jit.builder->getInt8PtrTy()); + Value* const destination = jit.builder->CreateBitCast(destFields, jit.builder->getInt8PtrTy()); // Copying the data - Value* copyArgs[] = { + Value* const copyArgs[] = { destination, source, dataSize, @@ -1157,8 +1161,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, jit.builder->getFalse() // not volatile }; - Type* memcpyType[] = {jit.builder->getInt8PtrTy(), jit.builder->getInt8PtrTy(), jit.builder->getInt32Ty() }; - Function* memcpyIntrinsic = getDeclaration(m_JITModule, Intrinsic::memcpy, memcpyType); + Type* const memcpyType[] = {jit.builder->getInt8PtrTy(), jit.builder->getInt8PtrTy(), jit.builder->getInt32Ty() }; + Function* const memcpyIntrinsic = getDeclaration(m_JITModule, Intrinsic::memcpy, memcpyType); jit.builder->CreateCall(memcpyIntrinsic, copyArgs); @@ -1170,27 +1174,27 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, break; case primitive::blockInvoke: { // 8 - Value* object = getArgument(jit); // jit.popValue(); - Value* block = jit.builder->CreateBitCast(object, m_baseTypes.block->getPointerTo()); + Value* const object = getArgument(jit); // jit.popValue(); + Value* const block = jit.builder->CreateBitCast(object, m_baseTypes.block->getPointerTo()); - int32_t argCount = jit.currentNode->getInstruction().getArgument() - 1; + const int32_t argCount = jit.currentNode->getInstruction().getArgument() - 1; - Value* blockAsContext = jit.builder->CreateBitCast(block, m_baseTypes.context->getPointerTo()); - Function* getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); - Value* blockTemps = jit.builder->CreateCall(getTempsFromContext, blockAsContext); + Value* const blockAsContext = jit.builder->CreateBitCast(block, m_baseTypes.context->getPointerTo()); + Function* const getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); + Value* const blockTemps = jit.builder->CreateCall(getTempsFromContext, blockAsContext); - Value* tempsSize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, blockTemps, "tempsSize."); + Value* const tempsSize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, blockTemps, "tempsSize."); - Value* argumentLocationPtr = jit.builder->CreateStructGEP(block, 1); - Value* argumentLocationField = jit.builder->CreateLoad(argumentLocationPtr); - Value* argumentLocationObject = jit.builder->CreateIntToPtr(argumentLocationField, m_baseTypes.object->getPointerTo()); - Value* argumentLocation = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, argumentLocationObject, "argLocation."); + Value* const argumentLocationPtr = jit.builder->CreateStructGEP(block, 1); + Value* const argumentLocationField = jit.builder->CreateLoad(argumentLocationPtr); + Value* const argumentLocationObject = jit.builder->CreateIntToPtr(argumentLocationField, m_baseTypes.object->getPointerTo()); + Value* const argumentLocation = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, argumentLocationObject, "argLocation."); - BasicBlock* tempsChecked = BasicBlock::Create(m_JITModule->getContext(), "tempsChecked.", jit.function); + BasicBlock* const tempsChecked = BasicBlock::Create(m_JITModule->getContext(), "tempsChecked.", jit.function); //Checking the passed temps size TODO unroll stack - Value* blockAcceptsArgCount = jit.builder->CreateSub(tempsSize, argumentLocation); - Value* tempSizeOk = jit.builder->CreateICmpSLE(jit.builder->getInt32(argCount), blockAcceptsArgCount); + Value* const blockAcceptsArgCount = jit.builder->CreateSub(tempsSize, argumentLocation); + Value* const tempSizeOk = jit.builder->CreateICmpSLE(jit.builder->getInt32(argCount), blockAcceptsArgCount); jit.builder->CreateCondBr(tempSizeOk, tempsChecked, primitiveFailedBB); jit.builder->SetInsertPoint(tempsChecked); @@ -1198,13 +1202,13 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, for (uint32_t index = argCount - 1, count = argCount; count > 0; index--, count--) { // (*blockTemps)[argumentLocation + index] = stack[--ec.stackTop]; - Value* fieldIndex = jit.builder->CreateAdd(argumentLocation, jit.builder->getInt32(index)); - Value* argument = getArgument(jit, index); // jit.popValue(); + Value* const fieldIndex = jit.builder->CreateAdd(argumentLocation, jit.builder->getInt32(index)); + Value* const argument = getArgument(jit, index); // jit.popValue(); jit.builder->CreateCall3(m_baseFunctions.setObjectField, blockTemps, fieldIndex, argument); } - Value* args[] = { block, jit.getCurrentContext() }; - Value* result = jit.builder->CreateCall(m_runtimeAPI.invokeBlock, args); + Value* const args[] = { block, jit.getCurrentContext() }; + Value* const result = jit.builder->CreateCall(m_runtimeAPI.invokeBlock, args); primitiveResult = result; } break; @@ -1214,12 +1218,12 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, //after calling cxa_throw. But! Someone may add Smalltalk code after <19> //Thats why we have to create unconditional br to 'primitiveFailed' //to catch any generated code into that BB - Value* contextPtr2Size = jit.builder->CreateTrunc(ConstantExpr::getSizeOf(m_baseTypes.context->getPointerTo()->getPointerTo()), jit.builder->getInt32Ty()); - Value* expnBuffer = jit.builder->CreateCall(m_exceptionAPI.cxa_allocate_exception, contextPtr2Size); - Value* expnTypedBuffer = jit.builder->CreateBitCast(expnBuffer, m_baseTypes.context->getPointerTo()->getPointerTo()); + Value* const contextPtr2Size = jit.builder->CreateTrunc(ConstantExpr::getSizeOf(m_baseTypes.context->getPointerTo()->getPointerTo()), jit.builder->getInt32Ty()); + Value* const expnBuffer = jit.builder->CreateCall(m_exceptionAPI.cxa_allocate_exception, contextPtr2Size); + Value* const expnTypedBuffer = jit.builder->CreateBitCast(expnBuffer, m_baseTypes.context->getPointerTo()->getPointerTo()); jit.builder->CreateStore(jit.getCurrentContext(), expnTypedBuffer); - Value* throwArgs[] = { + Value* const throwArgs[] = { expnBuffer, jit.builder->CreateBitCast(m_exceptionAPI.contextTypeInfo, jit.builder->getInt8PtrTy()), ConstantPointerNull::get(jit.builder->getInt8PtrTy()) @@ -1232,31 +1236,31 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, case primitive::arrayAt: // 24 case primitive::arrayAtPut: { // 5 std::size_t argIndex = (opcode == primitive::arrayAtPut) ? 2 : 1; - Value* indexObject = getArgument(jit, argIndex--); // jit.popValue(); - Value* arrayObject = getArgument(jit, argIndex--); // jit.popValue(); - Value* valueObejct = (opcode == primitive::arrayAtPut) ? getArgument(jit, argIndex--) : 0; + Value* const indexObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* const arrayObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* const valueObejct = (opcode == primitive::arrayAtPut) ? getArgument(jit, argIndex--) : 0; - BasicBlock* indexChecked = BasicBlock::Create(m_JITModule->getContext(), "indexChecked.", jit.function); + BasicBlock* const indexChecked = BasicBlock::Create(m_JITModule->getContext(), "indexChecked.", jit.function); //Checking whether index is Smallint //TODO jump to primitiveFailed if not - Value* indexIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, indexObject); + Value* const indexIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, indexObject); - Value* index = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, indexObject); - Value* actualIndex = jit.builder->CreateSub(index, jit.builder->getInt32(1)); + Value* const index = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, indexObject); + Value* const actualIndex = jit.builder->CreateSub(index, jit.builder->getInt32(1)); //Checking boundaries - Value* arraySize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, arrayObject); - Value* indexGEZero = jit.builder->CreateICmpSGE(actualIndex, jit.builder->getInt32(0)); - Value* indexLTSize = jit.builder->CreateICmpSLT(actualIndex, arraySize); - Value* boundaryOk = jit.builder->CreateAnd(indexGEZero, indexLTSize); + Value* const arraySize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, arrayObject); + Value* const indexGEZero = jit.builder->CreateICmpSGE(actualIndex, jit.builder->getInt32(0)); + Value* const indexLTSize = jit.builder->CreateICmpSLT(actualIndex, arraySize); + Value* const boundaryOk = jit.builder->CreateAnd(indexGEZero, indexLTSize); - Value* indexOk = jit.builder->CreateAnd(indexIsSmallInt, boundaryOk); + Value* const indexOk = jit.builder->CreateAnd(indexIsSmallInt, boundaryOk); jit.builder->CreateCondBr(indexOk, indexChecked, primitiveFailedBB); jit.builder->SetInsertPoint(indexChecked); if (opcode == primitive::arrayAtPut) { - Function* getObjectFieldPtr = m_JITModule->getFunction("getObjectFieldPtr"); - Value* fieldPointer = jit.builder->CreateCall2(getObjectFieldPtr, arrayObject, actualIndex); + Function* const getObjectFieldPtr = m_JITModule->getFunction("getObjectFieldPtr"); + Value* const fieldPointer = jit.builder->CreateCall2(getObjectFieldPtr, arrayObject, actualIndex); jit.builder->CreateCall2(m_runtimeAPI.checkRoot, valueObejct, fieldPointer); jit.builder->CreateStore(valueObejct, fieldPointer); @@ -1269,39 +1273,39 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, case primitive::stringAt: // 21 case primitive::stringAtPut: { // 22 std::size_t argIndex = (opcode == primitive::stringAtPut) ? 2 : 1; - Value* indexObject = getArgument(jit, argIndex--); // jit.popValue(); - Value* stringObject = getArgument(jit, argIndex--); // jit.popValue(); - Value* valueObejct = (opcode == primitive::stringAtPut) ? getArgument(jit, argIndex--) : 0; + Value* const indexObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* const stringObject = getArgument(jit, argIndex--); // jit.popValue(); + Value* const valueObejct = (opcode == primitive::stringAtPut) ? getArgument(jit, argIndex--) : 0; - BasicBlock* indexChecked = BasicBlock::Create(m_JITModule->getContext(), "indexChecked.", jit.function); + BasicBlock* const indexChecked = BasicBlock::Create(m_JITModule->getContext(), "indexChecked.", jit.function); //Checking whether index is Smallint //TODO jump to primitiveFailed if not - Value* indexIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, indexObject); + Value* const indexIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, indexObject); // Acquiring integer value of the index (from the smalltalk's TInteger) - Value* index = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, indexObject); - Value* actualIndex = jit.builder->CreateSub(index, jit.builder->getInt32(1)); + Value* const index = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, indexObject); + Value* const actualIndex = jit.builder->CreateSub(index, jit.builder->getInt32(1)); //Checking boundaries - Value* stringSize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, stringObject); - Value* indexGEZero = jit.builder->CreateICmpSGE(actualIndex, jit.builder->getInt32(0)); - Value* indexLTSize = jit.builder->CreateICmpSLT(actualIndex, stringSize); - Value* boundaryOk = jit.builder->CreateAnd(indexGEZero, indexLTSize); + Value* const stringSize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, stringObject); + Value* const indexGEZero = jit.builder->CreateICmpSGE(actualIndex, jit.builder->getInt32(0)); + Value* const indexLTSize = jit.builder->CreateICmpSLT(actualIndex, stringSize); + Value* const boundaryOk = jit.builder->CreateAnd(indexGEZero, indexLTSize); - Value* indexOk = jit.builder->CreateAnd(indexIsSmallInt, boundaryOk, "indexOk."); + Value* const indexOk = jit.builder->CreateAnd(indexIsSmallInt, boundaryOk, "indexOk."); jit.builder->CreateCondBr(indexOk, indexChecked, primitiveFailedBB); jit.builder->SetInsertPoint(indexChecked); // Getting access to the actual indexed byte location - Value* fields = jit.builder->CreateCall(m_baseFunctions.getObjectFields, stringObject); - Value* bytes = jit.builder->CreateBitCast(fields, jit.builder->getInt8PtrTy()); - Value* bytePtr = jit.builder->CreateGEP(bytes, actualIndex); + Value* const fields = jit.builder->CreateCall(m_baseFunctions.getObjectFields, stringObject); + Value* const bytes = jit.builder->CreateBitCast(fields, jit.builder->getInt8PtrTy()); + Value* const bytePtr = jit.builder->CreateGEP(bytes, actualIndex); if (opcode == primitive::stringAtPut) { // Popping new value from the stack, getting actual integral value from the TInteger // then shrinking it to the 1 byte representation and inserting into the pointed location - Value* valueInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, valueObejct); - Value* byte = jit.builder->CreateTrunc(valueInt, jit.builder->getInt8Ty()); + Value* const valueInt = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, valueObejct); + Value* const byte = jit.builder->CreateTrunc(valueInt, jit.builder->getInt8Ty()); jit.builder->CreateStore(byte, bytePtr); primitiveResult = stringObject; @@ -1310,8 +1314,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, // expanding it to the 4 byte integer and returning // as TInteger value - Value* byte = jit.builder->CreateLoad(bytePtr); - Value* expandedByte = jit.builder->CreateZExt(byte, jit.builder->getInt32Ty()); + Value* const byte = jit.builder->CreateLoad(bytePtr); + Value* const expandedByte = jit.builder->CreateZExt(byte, jit.builder->getInt32Ty()); primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, expandedByte); } } break; @@ -1327,19 +1331,19 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, case primitive::smallIntBitOr: // 36 case primitive::smallIntBitAnd: // 37 case primitive::smallIntBitShift: { // 39 - Value* rightObject = getArgument(jit, 1); // jit.popValue(); - Value* leftObject = getArgument(jit, 0); // jit.popValue(); + Value* const rightObject = getArgument(jit, 1); // jit.popValue(); + Value* const leftObject = getArgument(jit, 0); // jit.popValue(); compileSmallIntPrimitive(jit, opcode, leftObject, rightObject, primitiveResult, primitiveFailedBB); } break; case primitive::bulkReplace: { - Value* destination = getArgument(jit, 4); // jit.popValue(); - Value* sourceStartOffset = getArgument(jit, 3); // jit.popValue(); - Value* source = getArgument(jit, 2); // jit.popValue(); - Value* destinationStopOffset = getArgument(jit, 1); // jit.popValue(); - Value* destinationStartOffset = getArgument(jit, 0); // jit.popValue(); + Value* const destination = getArgument(jit, 4); // jit.popValue(); + Value* const sourceStartOffset = getArgument(jit, 3); // jit.popValue(); + Value* const source = getArgument(jit, 2); // jit.popValue(); + Value* const destinationStopOffset = getArgument(jit, 1); // jit.popValue(); + Value* const destinationStartOffset = getArgument(jit, 0); // jit.popValue(); - Value* arguments[] = { + Value* const arguments[] = { destination, destinationStartOffset, destinationStopOffset, @@ -1347,17 +1351,17 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, sourceStartOffset }; - Value* isBulkReplaceSucceeded = jit.builder->CreateCall(m_runtimeAPI.bulkReplace, arguments, "ok."); + Value* const isBulkReplaceSucceeded = jit.builder->CreateCall(m_runtimeAPI.bulkReplace, arguments, "ok."); primitiveResult = destination; primitiveFailed = jit.builder->CreateNot(isBulkReplaceSucceeded); } break; case primitive::LLVMsendMessage: { - Value* args = jit.builder->CreateBitCast( getArgument(jit, 1) /*jit.popValue()*/, m_baseTypes.objectArray->getPointerTo() ); - Value* selector = jit.builder->CreateBitCast( getArgument(jit, 0) /*jit.popValue()*/, m_baseTypes.symbol->getPointerTo() ); - Value* context = jit.getCurrentContext(); + Value* const args = jit.builder->CreateBitCast( getArgument(jit, 1) /*jit.popValue()*/, m_baseTypes.objectArray->getPointerTo() ); + Value* const selector = jit.builder->CreateBitCast( getArgument(jit, 0) /*jit.popValue()*/, m_baseTypes.symbol->getPointerTo() ); + Value* const context = jit.getCurrentContext(); - Value* sendMessageArgs[] = { + Value* const sendMessageArgs[] = { context, // calling context selector, args, @@ -1389,18 +1393,18 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, default: { // Here we need to create the arguments array from the values on the stack - uint8_t argumentsCount = jit.currentNode->getInstruction().getArgument(); - Value* argumentsObject = createArray(jit, argumentsCount); + const uint8_t argumentsCount = jit.currentNode->getInstruction().getArgument(); + Value* const argumentsObject = createArray(jit, argumentsCount); // Filling object with contents uint8_t index = argumentsCount; while (index > 0) { - Value* value = getArgument(jit); // jit.popValue(); - jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), value); + Value* const argument = getArgument(jit); // jit.popValue(); + jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), argument); } - Value* argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); - Value* primitiveFailedPtr = jit.builder->CreateAlloca(jit.builder->getInt1Ty(), 0, "primitiveFailedPtr"); + Value* const argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); + Value* const primitiveFailedPtr = jit.builder->CreateAlloca(jit.builder->getInt1Ty(), 0, "primitiveFailedPtr"); jit.builder->CreateStore(jit.builder->getFalse(), primitiveFailedPtr); primitiveResult = jit.builder->CreateCall3(m_runtimeAPI.callPrimitive, jit.builder->getInt8(opcode), argumentsArray, primitiveFailedPtr); @@ -1416,64 +1420,64 @@ void MethodCompiler::compileSmallIntPrimitive(TJITContext& jit, Value*& primitiveResult, BasicBlock* primitiveFailedBB) { - Value* rightIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, rightObject); - Value* leftIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, leftObject); - Value* areIntsCond = jit.builder->CreateAnd(rightIsInt, leftIsInt); + Value* const rightIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, rightObject); + Value* const leftIsInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, leftObject); + Value* const areIntsCond = jit.builder->CreateAnd(rightIsInt, leftIsInt); BasicBlock* areIntsBB = BasicBlock::Create(m_JITModule->getContext(), "areInts", jit.function); jit.builder->CreateCondBr(areIntsCond, areIntsBB, primitiveFailedBB); jit.builder->SetInsertPoint(areIntsBB); - Value* rightOperand = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, rightObject); - Value* leftOperand = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, leftObject); + Value* const rightOperand = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, rightObject); + Value* const leftOperand = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, leftObject); switch(opcode) { case primitive::smallIntAdd: { - Value* intResult = jit.builder->CreateAdd(leftOperand, rightOperand); + Value* const intResult = jit.builder->CreateAdd(leftOperand, rightOperand); //FIXME overflow primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult); } break; case primitive::smallIntDiv: { - Value* isZero = jit.builder->CreateICmpEQ(rightOperand, jit.builder->getInt32(0)); - BasicBlock* divBB = BasicBlock::Create(m_JITModule->getContext(), "div", jit.function); + Value* const isZero = jit.builder->CreateICmpEQ(rightOperand, jit.builder->getInt32(0)); + BasicBlock* divBB = BasicBlock::Create(m_JITModule->getContext(), "div", jit.function); jit.builder->CreateCondBr(isZero, primitiveFailedBB, divBB); jit.builder->SetInsertPoint(divBB); - Value* intResult = jit.builder->CreateSDiv(leftOperand, rightOperand); + Value* const intResult = jit.builder->CreateSDiv(leftOperand, rightOperand); primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult); } break; case primitive::smallIntMod: { - Value* isZero = jit.builder->CreateICmpEQ(rightOperand, jit.builder->getInt32(0)); - BasicBlock* modBB = BasicBlock::Create(m_JITModule->getContext(), "mod", jit.function); + Value* const isZero = jit.builder->CreateICmpEQ(rightOperand, jit.builder->getInt32(0)); + BasicBlock* modBB = BasicBlock::Create(m_JITModule->getContext(), "mod", jit.function); jit.builder->CreateCondBr(isZero, primitiveFailedBB, modBB); jit.builder->SetInsertPoint(modBB); - Value* intResult = jit.builder->CreateSRem(leftOperand, rightOperand); + Value* const intResult = jit.builder->CreateSRem(leftOperand, rightOperand); primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult); } break; case primitive::smallIntLess: { - Value* condition = jit.builder->CreateICmpSLT(leftOperand, rightOperand); + Value* const condition = jit.builder->CreateICmpSLT(leftOperand, rightOperand); primitiveResult = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); } break; case primitive::smallIntEqual: { - Value* condition = jit.builder->CreateICmpEQ(leftOperand, rightOperand); + Value* const condition = jit.builder->CreateICmpEQ(leftOperand, rightOperand); primitiveResult = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); } break; case primitive::smallIntMul: { - Value* intResult = jit.builder->CreateMul(leftOperand, rightOperand); + Value* const intResult = jit.builder->CreateMul(leftOperand, rightOperand); //FIXME overflow primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult); } break; case primitive::smallIntSub: { - Value* intResult = jit.builder->CreateSub(leftOperand, rightOperand); + Value* const intResult = jit.builder->CreateSub(leftOperand, rightOperand); primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult); } break; case primitive::smallIntBitOr: { - Value* intResult = jit.builder->CreateOr(leftOperand, rightOperand); + Value* const intResult = jit.builder->CreateOr(leftOperand, rightOperand); primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult); } break; case primitive::smallIntBitAnd: { - Value* intResult = jit.builder->CreateAnd(leftOperand, rightOperand); + Value* const intResult = jit.builder->CreateAnd(leftOperand, rightOperand); primitiveResult = jit.builder->CreateCall(m_baseFunctions.newInteger, intResult); } break; case primitive::smallIntBitShift: { @@ -1481,21 +1485,21 @@ void MethodCompiler::compileSmallIntPrimitive(TJITContext& jit, BasicBlock* shiftLeftBB = BasicBlock::Create(m_JITModule->getContext(), "<<", jit.function); BasicBlock* shiftResultBB = BasicBlock::Create(m_JITModule->getContext(), "shiftResult", jit.function); - Value* rightIsNeg = jit.builder->CreateICmpSLT(rightOperand, jit.builder->getInt32(0)); + Value* const rightIsNeg = jit.builder->CreateICmpSLT(rightOperand, jit.builder->getInt32(0)); jit.builder->CreateCondBr(rightIsNeg, shiftRightBB, shiftLeftBB); jit.builder->SetInsertPoint(shiftRightBB); - Value* rightOperandNeg = jit.builder->CreateNeg(rightOperand); - Value* shiftRightResult = jit.builder->CreateAShr(leftOperand, rightOperandNeg); + Value* const rightOperandNeg = jit.builder->CreateNeg(rightOperand); + Value* const shiftRightResult = jit.builder->CreateAShr(leftOperand, rightOperandNeg); jit.builder->CreateBr(shiftResultBB); jit.builder->SetInsertPoint(shiftLeftBB); - Value* shiftLeftResult = jit.builder->CreateShl(leftOperand, rightOperand); - Value* shiftLeftFailed = jit.builder->CreateICmpSGT(leftOperand, shiftLeftResult); + Value* const shiftLeftResult = jit.builder->CreateShl(leftOperand, rightOperand); + Value* const shiftLeftFailed = jit.builder->CreateICmpSGT(leftOperand, shiftLeftResult); jit.builder->CreateCondBr(shiftLeftFailed, primitiveFailedBB, shiftResultBB); jit.builder->SetInsertPoint(shiftResultBB); - PHINode* phi = jit.builder->CreatePHI(jit.builder->getInt32Ty(), 2); + PHINode* const phi = jit.builder->CreatePHI(jit.builder->getInt32Ty(), 2); phi->addIncoming(shiftRightResult, shiftRightBB); phi->addIncoming(shiftLeftResult, shiftLeftBB); @@ -1507,16 +1511,16 @@ void MethodCompiler::compileSmallIntPrimitive(TJITContext& jit, MethodCompiler::TStackObject MethodCompiler::allocateStackObject(llvm::IRBuilder<>& builder, uint32_t baseSize, uint32_t fieldsCount) { // Storing current edit location - BasicBlock* insertBlock = builder.GetInsertBlock(); + BasicBlock* const insertBlock = builder.GetInsertBlock(); BasicBlock::iterator insertPoint = builder.GetInsertPoint(); // Switching to the preamble - BasicBlock* preamble = insertBlock->getParent()->begin(); + BasicBlock* const preamble = insertBlock->getParent()->begin(); builder.SetInsertPoint(preamble, preamble->begin()); // Allocating the object slot const uint32_t holderSize = baseSize + sizeof(TObject*) * fieldsCount; - AllocaInst* objectSlot = builder.CreateAlloca(builder.getInt8Ty(), builder.getInt32(holderSize)); + AllocaInst* const objectSlot = builder.CreateAlloca(builder.getInt8Ty(), builder.getInt32(holderSize)); objectSlot->setAlignment(4); // Allocating object holder in the preamble @@ -1527,21 +1531,21 @@ MethodCompiler::TStackObject MethodCompiler::allocateStackObject(llvm::IRBuilder Function* gcrootIntrinsic = getDeclaration(m_JITModule, Intrinsic::gcroot); - //Value* structData = { ConstantInt::get(builder.getInt1Ty(), 1) }; + //Value* const structData = { ConstantInt::get(builder.getInt1Ty(), 1) }; // Registering holder in GC and supplying metadata that tells GC to treat this particular root // as a pointer to a stack object. Stack objects are not moved by GC. Instead, only their fields // and class pointer are updated. - //Value* metaData = ConstantStruct::get(m_JITModule->getTypeByName("TGCMetaData"), ConstantInt::get(builder.getInt1Ty(), 1)); - Value* metaData = m_JITModule->getGlobalVariable("stackObjectMeta"); - Value* stackRoot = builder.CreateBitCast(objectHolder, builder.getInt8PtrTy()->getPointerTo()); + //Value* const metaData = ConstantStruct::get(m_JITModule->getTypeByName("TGCMetaData"), ConstantInt::get(builder.getInt1Ty(), 1)); + Value* const metaData = m_JITModule->getGlobalVariable("stackObjectMeta"); + Value* const stackRoot = builder.CreateBitCast(objectHolder, builder.getInt8PtrTy()->getPointerTo()); builder.CreateCall2(gcrootIntrinsic, stackRoot, builder.CreateBitCast(metaData, builder.getInt8PtrTy())); // Returning to the original edit location builder.SetInsertPoint(insertBlock, insertPoint); // Storing the address of stack object to the holder - Value* newObject = builder.CreateBitCast(objectSlot, m_baseTypes.object->getPointerTo()); + Value* const newObject = builder.CreateBitCast(objectSlot, m_baseTypes.object->getPointerTo()); builder.CreateStore(newObject, objectHolder/*, true*/); TStackObject result; From 99a2263be6bd67f2e8065bbdbd7f30e4909ed889 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 12 Feb 2015 10:30:07 +0600 Subject: [PATCH 115/290] Fixes assign instructions linkage --- include/analysis.h | 9 +++++---- src/ControlGraph.cpp | 31 +++++++++++++++++-------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 6155f4d..d3b7bfc 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -243,17 +243,18 @@ class ControlDomain { m_localStack.push_back(value); } - ControlNode* popValue() { + ControlNode* topValue(bool keep = false) { assert(! m_localStack.empty()); ControlNode* value = m_localStack.back(); - m_localStack.pop_back(); + if (!keep) + m_localStack.pop_back(); return value; } - void requestArgument(std::size_t index, InstructionNode* forNode) { + void requestArgument(std::size_t index, InstructionNode* forNode, bool keep = false) { if (! m_localStack.empty()) { - ControlNode* argument = popValue(); + ControlNode* argument = topValue(keep); forNode->setArgument(index, argument); argument->addConsumer(forNode); diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 7f97533..3bbc631 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -114,9 +114,7 @@ void GraphConstructor::processNode(InstructionNode* node) case opcode::assignTemporary: // TODO Link with tau node case opcode::assignInstance: - // FIXME should not pop/push the stack - m_currentDomain->requestArgument(0, node); - m_currentDomain->pushValue(node); + m_currentDomain->requestArgument(0, node, true); break; case opcode::sendUnary: @@ -166,13 +164,16 @@ void GraphConstructor::processSpecials(InstructionNode* node) break; case special::sendToSuper: - case special::duplicate: m_currentDomain->requestArgument(0, node); m_currentDomain->pushValue(node); break; + case special::duplicate: + m_currentDomain->requestArgument(0, node, true); + m_currentDomain->pushValue(node); + break; + case special::popTop: - // m_currentDomain->popValue(); m_currentDomain->requestArgument(0, node); break; @@ -264,9 +265,10 @@ void GraphLinker::processNode(ControlNode& node) m_nodeToLink = 0; } - InstructionNode* const instruction = node.cast(); - if (instruction && instruction->getInstruction().isTerminator()) - return; // terminator nodes will take care of themselves + if (InstructionNode* const instruction = node.cast()) { + if (instruction->getInstruction().isTerminator()) + return; // terminator nodes will take care of themselves + } const TNodeSet& outEdges = node.getOutEdges(); TNodeSet::iterator iNode = outEdges.begin(); @@ -311,21 +313,22 @@ void GraphLinker::processArgumentRequests() void GraphLinker::processRequest(ControlDomain* domain, std::size_t argumentIndex, const ControlDomain::TArgumentRequest& request) { - ControlNode* const argument = getRequestedNode(domain, argumentIndex); + InstructionNode* const requestingNode = request.requestingNode; + ControlNode* const argument = getRequestedNode(domain, argumentIndex); const ControlNode::TNodeType argumentType = argument->getNodeType(); std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", argument->getIndex(), request.requestingNode->getIndex()); - request.requestingNode->setArgument(request.index, argument); - argument->addConsumer(request.requestingNode); + requestingNode->setArgument(request.index, argument); + argument->addConsumer(requestingNode); // We need to link the nodes only from the same domain // Cross domain references are handled separately - if (argument->getDomain() == request.requestingNode->getDomain()) - argument->addEdge(request.requestingNode); + if (argument->getDomain() == requestingNode->getDomain()) + argument->addEdge(requestingNode); if (argumentType == ControlNode::ntPhi) { - argument->addEdge(request.requestingNode); + argument->addEdge(requestingNode); // Registering phi as a consumer for all input nodes TNodeSet::iterator iNode = argument->getInEdges().begin(); From b6b9d7912024ecc5e0362a6d5e144b22dca061bb Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 12 Feb 2015 23:14:23 +0600 Subject: [PATCH 116/290] Fixes special::duplicate opcode handling in JIT --- src/JITRuntime.cpp | 2 ++ src/MethodCompiler.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index cebb5be..c58715d 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -362,6 +362,8 @@ TObject* JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TOb // Compiling function and storing it to the table for further use methodFunction = m_methodCompiler->compileMethod(method); + outs() << *methodFunction << "\n"; + verifyModule(*m_JITModule, AbortProcessAction); optimizeFunction(methodFunction); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 098dc23..a43a144 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -288,8 +288,6 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Processing the method's bytecodes writeFunctionBody(jit); - outs() << *jit.function; - // Cleaning up m_blockFunctions.clear(); m_targetToBlockMap.clear(); @@ -913,8 +911,10 @@ void MethodCompiler::doSpecial(TJITContext& jit) break; case special::duplicate: - // This should be completely eliminated by graph constructor - assert(false); + // Duplicating the origin value in the dup node. + // When dup consumers will be remapped to the real + // value in the ControlGraph this will be redundant. + setNodeValue(jit.currentNode, getNodeValue(jit, jit.currentNode->getArgument())); break; case special::popTop: From d01aa20b6d46701f2b47a08440aefd3503c175c3 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 14 Feb 2015 21:06:19 +0600 Subject: [PATCH 117/290] Fixes generation of phi node in MethodCompiler::getNodeValue() --- include/jit.h | 2 +- src/MethodCompiler.cpp | 70 ++++++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/include/jit.h b/include/jit.h index 24073ce..1f8c183 100644 --- a/include/jit.h +++ b/include/jit.h @@ -245,7 +245,7 @@ class MethodCompiler { TBaseFunctions m_baseFunctions; llvm::Value* processLeafNode(st::InstructionNode* instruction); - llvm::Value* getNodeValue(TJITContext& jit, st::ControlNode* node); + llvm::Value* getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock = 0); void setNodeValue(st::ControlNode* node, llvm::Value* value); llvm::Value* getArgument(TJITContext& jit, std::size_t index = 0); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index a43a144..2ea71fd 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -306,7 +306,7 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) llvm::BasicBlock* newBlock = m_jit.compiler->m_targetToBlockMap[domain.getBasicBlock()->getOffset()]; newBlock->moveAfter(m_jit.builder->GetInsertBlock()); // for a pretty sequenced BB output - m_jit.builder->SetInsertPoint(newBlock); + m_jit.builder->SetInsertPoint(newBlock, newBlock->getFirstInsertionPt()); return NodeVisitor::visitDomain(domain); } @@ -521,6 +521,11 @@ void MethodCompiler::doPushBlock(TJITContext& jit) ss << jit.originMethod->klass->name->toString() + ">>" + jit.originMethod->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); + { + ControlGraphVisualizer vis(blockContext.controlGraph, blockFunctionName + ".dot"); + vis.run(); + } + std::vector blockParams; blockParams.push_back(m_baseTypes.block->getPointerTo()); // block object with context information @@ -553,6 +558,8 @@ void MethodCompiler::doPushBlock(TJITContext& jit) writeFunctionBody(blockContext); + outs() << *blockContext.function << "\n"; + // Running optimization passes on a block function //JITRuntime::Instance()->optimizeFunction(blockContext.function); } @@ -652,35 +659,48 @@ llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = return getNodeValue(jit, jit.currentNode->getArgument(index)); } -Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node) +Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock /*= 0*/) { Value* value = node->getValue(); if (value) { - // If value is a holder, reading stored value. - // Otherwise, returning as is. - if (isa(value)) + // If value is a holder, loading and returning the stored value. + if (isa(value)) { + // In phi mode we should insert the load in the incoming block + if (insertBlock) { + TerminatorInst* const terminator = insertBlock->getTerminator(); + if (terminator) + jit.builder->SetInsertPoint(insertBlock, terminator); // inserting before terminator + else + jit.builder->SetInsertPoint(insertBlock); // appending to the end of the block + } + + // inserting in the current block return jit.builder->CreateLoad(value); - else - return value; + } + + // Otherwise, returning as is. + return value; } const std::size_t inEdgesCount = node->getInEdges().size(); if (st::InstructionNode* const instruction = node->cast()) { // If node is a leaf from the same domain we may encode it locally. - // If not, creating a dummy value wich will be replaced by real value later. + // If not, create a dummy value wich will be replaced by real value later. if (!inEdgesCount && instruction->getDomain() == jit.currentNode->getDomain()) - value = processLeafNode(instruction); + value = processLeafNode(instruction); // FIXME WTF? else value = UndefValue::get(jit.builder->getVoidTy()); - } else if (st::PhiNode* phiNode = node->cast()) { + } else if (st::PhiNode* const phiNode = node->cast()) { + // Storing insertion point to return to it later BasicBlock* const insertBlock = jit.builder->GetInsertBlock(); const BasicBlock::iterator storedInsertionPoint = jit.builder->GetInsertPoint(); - const BasicBlock::iterator firstInsertionPoint = insertBlock->getFirstInsertionPt(); - jit.builder->SetInsertPoint(insertBlock, firstInsertionPoint); - PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object, inEdgesCount); + // Switching to the phi insertion point + jit.builder->SetInsertPoint(insertBlock, insertBlock->getFirstInsertionPt()); + + PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), inEdgesCount, "phi."); st::TNodeSet::iterator iNode = phiNode->getInEdges().begin(); for (; iNode != phiNode->getInEdges().end(); ++iNode) { @@ -688,16 +708,25 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node) BasicBlock* const inBlock = m_targetToBlockMap[inNode->getDomain()->getBasicBlock()->getOffset()]; assert(inBlock); - phiValue->addIncoming(getNodeValue(jit, inNode), inBlock); + // This call may change the insertion point of one of the incoming values is a value holder, + // not just a simple value. Load should be inserted in the incoming basic block. + phiValue->addIncoming(getNodeValue(jit, inNode, inBlock), inBlock); } - phiNode->setValue(phiValue); - jit.builder->SetInsertPoint(insertBlock, storedInsertionPoint); + // Phi is created at the top of the basic block but consumed later. + // Value will be broken if GC will be invoked between phi and consumer. + // Resetting the insertion point which may be changed in getNodeValue() before. + jit.builder->SetInsertPoint(insertBlock, insertBlock->getFirstInsertionPt()); + Value* const phiHolder = protectPointer(jit, phiValue); - value = phiValue; + // Finally, restoring original insertion point and loading the value from phi + jit.builder->SetInsertPoint(insertBlock, storedInsertionPoint); + value = jit.builder->CreateLoad(phiHolder); } assert(value); + node->setValue(value); + return value; } @@ -1193,8 +1222,8 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, BasicBlock* const tempsChecked = BasicBlock::Create(m_JITModule->getContext(), "tempsChecked.", jit.function); //Checking the passed temps size TODO unroll stack - Value* const blockAcceptsArgCount = jit.builder->CreateSub(tempsSize, argumentLocation); - Value* const tempSizeOk = jit.builder->CreateICmpSLE(jit.builder->getInt32(argCount), blockAcceptsArgCount); + Value* const blockAcceptsArgCount = jit.builder->CreateSub(tempsSize, argumentLocation, "blockAcceptsArgCount."); + Value* const tempSizeOk = jit.builder->CreateICmpSLE(jit.builder->getInt32(argCount), blockAcceptsArgCount, "tempSizeOk."); jit.builder->CreateCondBr(tempSizeOk, tempsChecked, primitiveFailedBB); jit.builder->SetInsertPoint(tempsChecked); @@ -1202,8 +1231,9 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, for (uint32_t index = argCount - 1, count = argCount; count > 0; index--, count--) { // (*blockTemps)[argumentLocation + index] = stack[--ec.stackTop]; - Value* const fieldIndex = jit.builder->CreateAdd(argumentLocation, jit.builder->getInt32(index)); + Value* const fieldIndex = jit.builder->CreateAdd(argumentLocation, jit.builder->getInt32(index), "fieldIndex."); Value* const argument = getArgument(jit, index); // jit.popValue(); + argument->setName("argument."); jit.builder->CreateCall3(m_baseFunctions.setObjectField, blockTemps, fieldIndex, argument); } From 3ee974c55b3a8c470e4474fbf8203b00f61cc7ec Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 14 Feb 2015 21:08:53 +0600 Subject: [PATCH 118/290] Comments out flood logs in control graph --- src/ControlGraph.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 3bbc631..fb84ae2 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -58,11 +58,11 @@ class GraphConstructor : public InstructionVisitor { m_currentDomain->addNode(newNode); // Processing instruction by adding references - std::printf("GraphConstructor::visitInstruction : processing node %.2u %s%s \n", - newNode->getIndex(), - newNode->getInstruction().isBranch() ? "^" : "", - newNode->getInstruction().isTerminator() ? "!" : "" - ); +// std::printf("GraphConstructor::visitInstruction : processing node %.2u %s%s \n", +// newNode->getIndex(), +// newNode->getInstruction().isBranch() ? "^" : "", +// newNode->getInstruction().isTerminator() ? "!" : "" +// ); processNode(newNode); return true; @@ -260,7 +260,7 @@ void GraphLinker::processNode(ControlNode& node) // Linking pending node if (m_nodeToLink) { - std::printf("GraphLinker::processNode : linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); +// std::printf("GraphLinker::processNode : linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); m_nodeToLink->addEdge(&node); m_nodeToLink = 0; } From c55c6af99f5965f2759872568bb197d91920f089 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 15 Feb 2015 12:59:28 +0600 Subject: [PATCH 119/290] Fixes block invoke primitive --- src/MethodCompiler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 2ea71fd..572b548 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -1072,7 +1072,8 @@ void MethodCompiler::doPrimitive(TJITContext& jit) jit.builder->CreateRet(primitiveResult); jit.builder->SetInsertPoint(primitiveFailedBB); - setNodeValue(jit.currentNode, primitiveResult); + // FIXME Are we really allowed to use the value without holder? + setNodeValue(jit.currentNode, primitiveResult); // jit.pushValue(m_globals.nilObject); } @@ -1206,7 +1207,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, Value* const object = getArgument(jit); // jit.popValue(); Value* const block = jit.builder->CreateBitCast(object, m_baseTypes.block->getPointerTo()); - const int32_t argCount = jit.currentNode->getInstruction().getArgument() - 1; + const uint32_t argCount = jit.currentNode->getInstruction().getArgument() - 1; Value* const blockAsContext = jit.builder->CreateBitCast(block, m_baseTypes.context->getPointerTo()); Function* const getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); @@ -1220,6 +1221,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, Value* const argumentLocation = jit.builder->CreateCall(m_baseFunctions.getIntegerValue, argumentLocationObject, "argLocation."); BasicBlock* const tempsChecked = BasicBlock::Create(m_JITModule->getContext(), "tempsChecked.", jit.function); + tempsChecked->moveAfter(jit.builder->GetInsertBlock()); //Checking the passed temps size TODO unroll stack Value* const blockAcceptsArgCount = jit.builder->CreateSub(tempsSize, argumentLocation, "blockAcceptsArgCount."); @@ -1232,7 +1234,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, { // (*blockTemps)[argumentLocation + index] = stack[--ec.stackTop]; Value* const fieldIndex = jit.builder->CreateAdd(argumentLocation, jit.builder->getInt32(index), "fieldIndex."); - Value* const argument = getArgument(jit, index); // jit.popValue(); + Value* const argument = getArgument(jit, index + 1); // jit.popValue(); argument->setName("argument."); jit.builder->CreateCall3(m_baseFunctions.setObjectField, blockTemps, fieldIndex, argument); } @@ -1308,6 +1310,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, Value* const valueObejct = (opcode == primitive::stringAtPut) ? getArgument(jit, argIndex--) : 0; BasicBlock* const indexChecked = BasicBlock::Create(m_JITModule->getContext(), "indexChecked.", jit.function); + indexChecked->moveAfter(jit.builder->GetInsertBlock()); //Checking whether index is Smallint //TODO jump to primitiveFailed if not Value* const indexIsSmallInt = jit.builder->CreateCall(m_baseFunctions.isSmallInteger, indexObject); From 44aa4aedc8bafc9dde404f8cebfba718fec190ea Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 16 Feb 2015 21:43:33 +0600 Subject: [PATCH 120/290] Refactors ControlGraph traces to be configurable --- src/ControlGraph.cpp | 110 +++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 40 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index fb84ae2..a179dc6 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -3,6 +3,8 @@ using namespace st; +static const bool traces_enabled = false; + bool NodeIndexCompare::operator() (const ControlNode* a, const ControlNode* b) { return a->getIndex() < b->getIndex(); @@ -46,7 +48,9 @@ class GraphConstructor : public InstructionVisitor { m_currentDomain = m_graph->getDomainFor(&basicBlock); m_currentDomain->setBasicBlock(&basicBlock); - std::printf("GraphConstructor::visitBlock : block %p (%.2u), domain %p\n", &basicBlock, basicBlock.getOffset(), m_currentDomain); + if (traces_enabled) + std::printf("GraphConstructor::visitBlock : block %p (%.2u), domain %p\n", &basicBlock, basicBlock.getOffset(), m_currentDomain); + return InstructionVisitor::visitBlock(basicBlock); } @@ -58,11 +62,13 @@ class GraphConstructor : public InstructionVisitor { m_currentDomain->addNode(newNode); // Processing instruction by adding references -// std::printf("GraphConstructor::visitInstruction : processing node %.2u %s%s \n", -// newNode->getIndex(), -// newNode->getInstruction().isBranch() ? "^" : "", -// newNode->getInstruction().isTerminator() ? "!" : "" -// ); + if (traces_enabled) + std::printf("GraphConstructor::visitInstruction : processing node %.2u %s%s \n", + newNode->getIndex(), + newNode->getInstruction().isBranch() ? "^" : "", + newNode->getInstruction().isTerminator() ? "!" : "" + ); + processNode(newNode); return true; @@ -213,17 +219,19 @@ class GraphLinker : public NodeVisitor { virtual bool visitDomain(ControlDomain& domain) { m_currentDomain = &domain; - std::printf("GraphLinker::visitDomain : processing domain %p, block offset %.2u, referrers %u, local stack %u, requested args %.2u\n", - &domain, - domain.getBasicBlock()->getOffset(), - domain.getBasicBlock()->getReferers().size(), - domain.getLocalStack().size(), - domain.getRequestedArguments().size() - ); - - for (std::size_t index = 0; index < domain.getRequestedArguments().size(); index++) - std::printf("GraphLinker::visitDomain : arg request %u, node index %.2u\n", - index, domain.getRequestedArguments()[index].requestingNode->getIndex()); + if (traces_enabled) { + std::printf("GraphLinker::visitDomain : processing domain %p, block offset %.2u, referrers %u, local stack %u, requested args %.2u\n", + &domain, + domain.getBasicBlock()->getOffset(), + domain.getBasicBlock()->getReferers().size(), + domain.getLocalStack().size(), + domain.getRequestedArguments().size() + ); + + for (std::size_t index = 0; index < domain.getRequestedArguments().size(); index++) + std::printf("GraphLinker::visitDomain : arg request %u, node index %.2u\n", + index, domain.getRequestedArguments()[index].requestingNode->getIndex()); + } processBranching(); processArgumentRequests(); @@ -260,7 +268,9 @@ void GraphLinker::processNode(ControlNode& node) // Linking pending node if (m_nodeToLink) { -// std::printf("GraphLinker::processNode : linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); + if (traces_enabled) + std::printf("GraphLinker::processNode : linking nodes %.2u and %.2u\n", m_nodeToLink->getIndex(), node.getIndex()); + m_nodeToLink->addEdge(&node); m_nodeToLink = 0; } @@ -299,7 +309,9 @@ void GraphLinker::processBranching() InstructionNode* const terminator = refererDomain->getTerminator(); assert(terminator && terminator->getInstruction().isBranch()); - std::printf("GraphLinker::processNode : linking nodes of referring graphs %.2u and %.2u\n", terminator->getIndex(), entryPoint->getIndex()); + if (traces_enabled) + std::printf("GraphLinker::processNode : linking nodes of referring graphs %.2u and %.2u\n", terminator->getIndex(), entryPoint->getIndex()); + terminator->addEdge(entryPoint); } } @@ -317,7 +329,8 @@ void GraphLinker::processRequest(ControlDomain* domain, std::size_t argumentInde ControlNode* const argument = getRequestedNode(domain, argumentIndex); const ControlNode::TNodeType argumentType = argument->getNodeType(); - std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", argument->getIndex(), request.requestingNode->getIndex()); + if (traces_enabled) + std::printf("GraphLinker::processNode : linking nodes of argument request %.2u and %.2u\n", argument->getIndex(), request.requestingNode->getIndex()); requestingNode->setArgument(request.index, argument); argument->addConsumer(requestingNode); @@ -416,15 +429,19 @@ class GraphOptimizer : public PlainNodeVisitor { const TNodeSet& consumers = instruction->getConsumers(); if (consumers.empty()) { - std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); + if (traces_enabled) + std::printf("GraphOptimizer::visitNode : node %u is not consumed and may be removed\n", instruction->getIndex()); + m_nodesToRemove.push_back(instruction); } else if (consumers.size() == 1) { if (InstructionNode* const consumer = (*consumers.begin())->cast()) { const TSmalltalkInstruction& consumerInstruction = consumer->getInstruction(); if (consumerInstruction == TSmalltalkInstruction(opcode::doSpecial, special::popTop)) { - std::printf("GraphOptimizer::visitNode : node %u is consumed only by popTop %u and may be removed\n", - instruction->getIndex(), - consumer->getIndex()); + if (traces_enabled) + std::printf("GraphOptimizer::visitNode : node %u is consumed only by popTop %u and may be removed\n", + instruction->getIndex(), + consumer->getIndex() + ); m_nodesToRemove.push_back(consumer); m_nodesToRemove.push_back(instruction); @@ -433,7 +450,9 @@ class GraphOptimizer : public PlainNodeVisitor { } } else if (PhiNode* const phi = node.cast()) { if (phi->getInEdges().size() == 1) { - std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", phi->getIndex()); + if (traces_enabled) + std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", phi->getIndex()); + m_nodesToRemove.push_back(phi); } } @@ -462,10 +481,12 @@ class GraphOptimizer : public PlainNodeVisitor { InstructionNode* const valueTarget = (*phi->getOutEdges().begin())->cast(); assert(valueTarget); - std::printf("Skipping phi node %.2u and remapping edges to link values directly: %.2u <-- %.2u\n", - phi->getIndex(), - valueSource->getIndex(), - valueTarget->getIndex()); + if (traces_enabled) + std::printf("Skipping phi node %.2u and remapping edges to link values directly: %.2u <-- %.2u\n", + phi->getIndex(), + valueSource->getIndex(), + valueTarget->getIndex() + ); valueSource->removeEdge(phi); phi->removeEdge(valueTarget); @@ -495,10 +516,12 @@ class GraphOptimizer : public PlainNodeVisitor { while (iNode != node->getInEdges().end()) { ControlNode* const sourceNode = *iNode++; - std::printf("Remapping node %.2u from %.2u to %.2u\n", - sourceNode->getIndex(), - node->getIndex(), - nextNode->getIndex()); + if (traces_enabled) + std::printf("Remapping node %.2u from %.2u to %.2u\n", + sourceNode->getIndex(), + node->getIndex(), + nextNode->getIndex() + ); sourceNode->removeEdge(node); sourceNode->addEdge(nextNode); @@ -510,15 +533,16 @@ class GraphOptimizer : public PlainNodeVisitor { { ControlNode* const targetNode = *iNode++; - std::printf("Erasing edge %.2u -> %.2u\n", - node->getIndex(), - targetNode->getIndex()); + if (traces_enabled) + std::printf("Erasing edge %.2u -> %.2u\n", node->getIndex(), targetNode->getIndex()); node->removeEdge(targetNode); } + if (traces_enabled) + std::printf("Erasing node %.2u\n", node->getIndex()); + // Removing node from the graph - std::printf("Erasing node %.2u\n", node->getIndex()); node->getDomain()->removeNode(node); m_graph->eraseNode(node); } @@ -529,21 +553,27 @@ class GraphOptimizer : public PlainNodeVisitor { void ControlGraph::buildGraph() { + if (traces_enabled) + std::printf("Phase 1. Constructing control graph\n"); + // Iterating through basic blocks of parsed method and constructing node domains - std::printf("Phase 1. Constructing control graph\n"); GraphConstructor constructor(this); constructor.run(); + if (traces_enabled) + std::printf("Phase 2. Linking control graph\n"); + // Linking nodes that requested argument during previous stage. // They're linked using phi nodes or a direct link if possible. // Also branching edges are added so graph remains linked even if // no stack relations exist. - std::printf("Phase 2. Linking control graph\n"); GraphLinker linker(this); linker.run(); + if (traces_enabled) + std::printf("Phase 3. Optimizing control graph\n"); + // Optimizing graph by removing stalled nodes and merging linear branch sequences - std::printf("Phase 3. Optimizing control graph\n"); GraphOptimizer optimizer(this); optimizer.run(); } From 46d10294125211a3c44ba1e5943e6c7346064614 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 21 Feb 2015 12:36:55 +0600 Subject: [PATCH 121/290] Adds incoming data to st::PhiNode Issue: #32 --- include/analysis.h | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index d3b7bfc..9595005 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -2,6 +2,8 @@ #define LLST_ANALYSIS_INCLUDED #include +#include + #include namespace llvm { @@ -202,13 +204,33 @@ class PushBlockNode : public InstructionNode { // to a node having a phi node as it's agrument. class PhiNode : public ControlNode { public: - PhiNode(uint32_t index) : ControlNode(index) { } + PhiNode(uint32_t index) : ControlNode(index) { m_incomingList.reserve(2); } virtual TNodeType getNodeType() const { return ntPhi; } uint32_t getPhiIndex() const { return m_phiIndex; } void setPhiIndex(uint32_t value) { m_phiIndex = value; } + struct TIncoming { + ControlDomain* domain; + ControlNode* node; + }; + + typedef std::vector TIncomingList; + + // Value node may or may not belong to the specified domain + void addIncoming(ControlDomain* domain, ControlNode* value) { + TIncoming incoming; + incoming.domain = domain; + incoming.node = value; + + m_incomingList.push_back(incoming); + } + + const TIncomingList& getIncomingList() const { return m_incomingList; } + TNodeSet getRealValues(); + private: - uint32_t m_phiIndex; + uint32_t m_phiIndex; + TIncomingList m_incomingList; }; // Tau node is reserved for further use in type inference subsystem. From 94c732a304bc2f7925e8f7d9474fee85784e01c8 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 21 Feb 2015 18:54:35 +0600 Subject: [PATCH 122/290] Modifies phi linking rules Phi nodes now contain incoming data including domain and node. Issue: #32 --- src/ControlGraph.cpp | 52 +++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index a179dc6..e401870 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -39,6 +39,21 @@ template<> PushBlockNode* ControlGraph::newNode() { return static_cast(node); } +TNodeSet PhiNode::getRealValues() { + TNodeSet values; + + for (std::size_t i = 0; i < m_incomingList.size(); i++) { + if (PhiNode* const phi = m_incomingList[i].node->cast()) { + const TNodeSet& realValues = phi->getRealValues(); + values.insert(realValues.begin(), realValues.end()); + } else { + values.insert(m_incomingList[i].node); + } + } + + return values; +} + class GraphConstructor : public InstructionVisitor { public: GraphConstructor(ControlGraph* graph) @@ -337,17 +352,8 @@ void GraphLinker::processRequest(ControlDomain* domain, std::size_t argumentInde // We need to link the nodes only from the same domain // Cross domain references are handled separately - if (argument->getDomain() == requestingNode->getDomain()) - argument->addEdge(requestingNode); - - if (argumentType == ControlNode::ntPhi) { + if (requestingNode->getNodeType() == ControlNode::ntPhi || argumentType == ControlNode::ntPhi || argument->getDomain() == requestingNode->getDomain()) argument->addEdge(requestingNode); - - // Registering phi as a consumer for all input nodes - TNodeSet::iterator iNode = argument->getInEdges().begin(); - for (; iNode != argument->getInEdges().end(); ++iNode) - (*iNode)->addConsumer(argument); - } } void GraphLinker::mergePhi(PhiNode* source, PhiNode* target) @@ -376,8 +382,8 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar BasicBlock::TBasicBlockSet::iterator iBlock = refererBlocks.begin(); for (; iBlock != refererBlocks.end(); ++iBlock) { - ControlDomain* const refererDomain = m_graph->getDomainFor(* iBlock); - const TNodeList& refererStack = refererDomain->getLocalStack(); + ControlDomain* const refererDomain = m_graph->getDomainFor(* iBlock); + const TNodeList& refererStack = refererDomain->getLocalStack(); const std::size_t refererStackSize = refererStack.size(); if (!refererStackSize || argumentIndex > refererStackSize - 1) { @@ -390,10 +396,10 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar if (singleReferer) { result = refererValue; } else { - // Nested phi nodes should be merged together - if (PhiNode* const phi = refererValue->cast()) - mergePhi(phi, static_cast(result)); - else + result->cast()->addIncoming(refererDomain, refererValue); + refererValue->addConsumer(result); + + if (refererValue->getNodeType() == ControlNode::ntPhi) refererValue->addEdge(result); } @@ -402,10 +408,15 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar assert(valueIndex < refererStackSize); ControlNode* const stackValue = refererStack[valueIndex]; - if (singleReferer) + if (singleReferer) { result = stackValue; - else - stackValue->addEdge(result); + } else { + result->cast()->addIncoming(refererDomain, stackValue); + stackValue->addConsumer(result); + + if (stackValue->getNodeType() == ControlNode::ntPhi) + stackValue->addEdge(result); + } } } @@ -453,7 +464,8 @@ class GraphOptimizer : public PlainNodeVisitor { if (traces_enabled) std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", phi->getIndex()); - m_nodesToRemove.push_back(phi); + // FIXME Should be modified according to new phi linking rules + // m_nodesToRemove.push_back(phi); } } From 74db8296c67a079828bc4532adfa9558ab631bf8 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 21 Feb 2015 19:01:43 +0600 Subject: [PATCH 123/290] Fixes visualization of new phi nodes Issue: #32 --- include/visualization.h | 4 ++-- src/ControlGraphVisualizer.cpp | 43 ++++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/include/visualization.h b/include/visualization.h index 5cc2016..01f3cd0 100644 --- a/include/visualization.h +++ b/include/visualization.h @@ -8,9 +8,9 @@ #include #include -class ControlGraphVisualizer : public st::NodeVisitor { +class ControlGraphVisualizer : public st::PlainNodeVisitor { public: - ControlGraphVisualizer(st::ControlGraph* graph, const std::string& fileName) : st::NodeVisitor(graph) { + ControlGraphVisualizer(st::ControlGraph* graph, const std::string& fileName) : st::PlainNodeVisitor(graph) { m_stream.open(fileName.c_str(), std::ios::out | std::ios::trunc); m_stream << "digraph G2 {\n"; diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 24aae53..06e2460 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -1,25 +1,29 @@ #include -bool ControlGraphVisualizer::visitDomain(st::ControlDomain& domain) { +bool ControlGraphVisualizer::visitDomain(st::ControlDomain& /*domain*/) { // if (!firstDomain) // m_stream << "\t} \n" << std::endl; // closing subgraph firstDomain = false; // m_stream << "\n\tsubgraph cluster_" << domain.getBasicBlock()->getOffset() << " {\n"; - return st::NodeVisitor::visitDomain(domain); +// return st::NodeVisitor::visitDomain(domain); + return false; } std::string edgeStyle(st::ControlNode* from, st::ControlNode* to) { const st::InstructionNode* const fromInstruction = from->cast(); const st::InstructionNode* const toInstruction = to->cast(); - if ((fromInstruction && fromInstruction->getInstruction().isBranch()) || - (toInstruction && toInstruction->getArgumentsCount() == 0)) - { +// if (from->getNodeType() == st::ControlNode::ntPhi) +// return "[color=\"black\"]"; + + if (fromInstruction && fromInstruction->getInstruction().isBranch()) return "[color=\"grey\" style=\"dashed\"]"; - } + + if (toInstruction && toInstruction->getArgumentsCount() == 0) + return "[color=\"black\" style=\"dashed\" ]"; return ""; } @@ -57,10 +61,24 @@ bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { m_stream << " labelfloat=true color=\"blue\" fontcolor=\"blue\" style=\"dashed\" constraint=false];\n"; } + } else if (const st::PhiNode* const phi = node.cast()) { + const st::PhiNode::TIncomingList& incomingList = phi->getIncomingList(); + for (std::size_t index = 0; index < incomingList.size(); index++) { + const st::PhiNode::TIncoming& incoming = incomingList[index]; + + m_stream << "\t\t" << incoming.node->getIndex() << " -> " << node.getIndex() << " [" + << "dir=back labelfloat=true color=\"blue\" fontcolor=\"blue\" style=\"dashed\" constraint=true ];\n"; + + m_stream << "\t\t" << incoming.domain->getTerminator()->getIndex() << " -> " << phi->getIndex() << " [" + << "style=\"invis\" constraint=true ];\n"; + +// m_stream << "\t\t" << node.getIndex() << " -> " << incoming.value->getIndex() << " [" +// << " labelfloat=true color=\"blue\" fontcolor=\"blue\" style=\"dashed\" constraint=false ];\n"; + } } markNode(&node); - return st::NodeVisitor::visitNode(node); + return st::PlainNodeVisitor::visitNode(node); } bool ControlGraphVisualizer::isNodeProcessed(st::ControlNode* node) { @@ -71,11 +89,13 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { // Setting node label std::string label; std::string color; + std::string shape = "box"; switch (node->getNodeType()) { case st::ControlNode::ntPhi: - label = "Phi "; + //label = "Phi "; color = "grey"; + shape = "oval"; break; case st::ControlNode::ntTau: @@ -108,8 +128,11 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { ; } - m_stream << "\t\t" << node->getIndex() << " [shape=box label=\"" << node->getDomain()->getBasicBlock()->getOffset() << "." << node->getIndex() << " : " << label << "\" color=\"" << color << "\"];\n"; -// m_stream << "\t\t " << node->getIndex() << "[label=\"" << node->getIndex() /*<< " : " << label */<< "\" color=\"" << color << "\"];\n"; + if (node->getNodeType() == st::ControlNode::ntPhi) + m_stream << "\t\t" << node->getIndex() << " [label=\"" << node->getIndex() << "\" color=\"" << color << "\"];\n"; + else + m_stream << "\t\t" << node->getIndex() << " [shape=\"" << shape << "\" label=\"" << (node->getDomain() ? node->getDomain()->getBasicBlock()->getOffset() : 666) << "." << node->getIndex() << " : " << label << "\" color=\"" << color << "\"];\n"; + m_processedNodes[node] = true; } From b1311e501252127cfb228bc6df1aee77d106d2f2 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 21 Feb 2015 19:03:11 +0600 Subject: [PATCH 124/290] Fixes MethodCompiler to conform new phi nodes Issue: #32 --- src/MethodCompiler.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 572b548..2d7ab11 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -242,7 +242,9 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF jit.function = methodFunction ? methodFunction : createFunction(method); { - ControlGraphVisualizer vis(jit.controlGraph, std::string(jit.function->getName()) + ".dot"); + std::ostringstream ss; + ss << "dots/" << jit.function->getName().data() << ".dot"; + ControlGraphVisualizer vis(jit.controlGraph, ss.str()); vis.run(); } @@ -522,7 +524,9 @@ void MethodCompiler::doPushBlock(TJITContext& jit) std::string blockFunctionName = ss.str(); { - ControlGraphVisualizer vis(blockContext.controlGraph, blockFunctionName + ".dot"); + std::ostringstream ss; + ss << "dots/" << blockFunctionName << ".dot"; + ControlGraphVisualizer vis(blockContext.controlGraph, ss.str()); vis.run(); } @@ -702,15 +706,16 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), inEdgesCount, "phi."); - st::TNodeSet::iterator iNode = phiNode->getInEdges().begin(); - for (; iNode != phiNode->getInEdges().end(); ++iNode) { - st::ControlNode* const inNode = *iNode; - BasicBlock* const inBlock = m_targetToBlockMap[inNode->getDomain()->getBasicBlock()->getOffset()]; + const st::PhiNode::TIncomingList& incomingList = phiNode->getIncomingList(); + for (std::size_t index = 0; index < incomingList.size(); index++) { + const st::PhiNode::TIncoming& incoming = incomingList[index]; + + BasicBlock* const inBlock = m_targetToBlockMap[incoming.domain->getBasicBlock()->getOffset()]; assert(inBlock); // This call may change the insertion point of one of the incoming values is a value holder, // not just a simple value. Load should be inserted in the incoming basic block. - phiValue->addIncoming(getNodeValue(jit, inNode, inBlock), inBlock); + phiValue->addIncoming(getNodeValue(jit, incoming.node, inBlock), inBlock); } // Phi is created at the top of the basic block but consumed later. From f6d0beee36abb769f470a583ada2bc963e1c58a8 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 21 Feb 2015 19:07:57 +0600 Subject: [PATCH 125/290] Adds trace control constant to ParsedBytecode.cpp Issue: #32 --- src/ParsedBytecode.cpp | 72 ++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 5bb69ba..90ec481 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -3,6 +3,8 @@ using namespace st; +static const bool traces_enabled = false; + void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { assert(m_origin && m_origin->byteCodes); @@ -11,7 +13,8 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { TByteObject& byteCodes = * m_origin->byteCodes; const uint16_t stopPointer = stopOffset ? stopOffset : byteCodes.getSize(); - std::printf("Phase 1. Collecting branch instructions and building blocks\n"); + if (traces_enabled) + std::printf("Phase 1. Collecting branch instructions and building blocks\n"); // Scaning the method's bytecodes for branch sites and collecting branch targets. // Creating target basic blocks beforehand and collecting them in a map. @@ -25,11 +28,13 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Extra holds the bytecode offset right after the block const uint16_t blockStopOffset = instruction.getExtra(); + if (traces_enabled) + std::printf("%.4u : Parsing smalltalk block in interval [%u:%u)\n", currentBytePointer, blockStartOffset, blockStopOffset); + // Parsing block. This operation depends on // whether we're in a method or in a block. // Nested blocks are registered in the // container method, not the outer block. - std::printf("%.4u : Parsing smalltalk block in interval [%u:%u)\n", currentBytePointer, blockStartOffset, blockStopOffset); parseBlock(blockStartOffset, blockStopOffset); // Skipping the nested block's bytecodes @@ -47,19 +52,24 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Processing skip block const uint16_t skipOffset = decoder.getBytePointer(); BasicBlock* skipBasicBlock = createBasicBlock(skipOffset); - std::printf("%.4u : branch to skip block %p (%u)\n", currentBytePointer, skipBasicBlock, skipOffset); + + if (traces_enabled) + std::printf("%.4u : branch to skip block %p (%u)\n", currentBytePointer, skipBasicBlock, skipOffset); } // no break here case special::branch: { // Processing target block const uint16_t targetOffset = instruction.getExtra(); BasicBlock* targetBasicBlock = createBasicBlock(targetOffset); - std::printf("%.4u : branch to target block %p (%u)\n", currentBytePointer, targetBasicBlock, targetOffset); + + if (traces_enabled) + std::printf("%.4u : branch to target block %p (%u)\n", currentBytePointer, targetBasicBlock, targetOffset); } break; } } - std::printf("Phase 2. Populating blocks with instructions\n"); + if (traces_enabled) + std::printf("Phase 2. Populating blocks with instructions\n"); // Populating previously created basic blocks with actual instructions BasicBlock* currentBasicBlock = m_offsetToBasicBlock[startOffset]; @@ -68,13 +78,16 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // If no branch site points to start offset then we create block ourselves if (! currentBasicBlock) { m_offsetToBasicBlock[startOffset] = currentBasicBlock = new BasicBlock(startOffset); -// std::printf("created start basic block %p (%u)\n", currentBasicBlock, startOffset); + + if (traces_enabled) + std::printf("created start basic block %p (%u)\n", currentBasicBlock, startOffset); // Pushing block from the beginning to comply it's position m_basicBlocks.push_front(currentBasicBlock); } - std::printf("Initial block is %p offset %u\n", currentBasicBlock, currentBasicBlock->getOffset()); + if (traces_enabled) + std::printf("Initial block is %p offset %u\n", currentBasicBlock, currentBasicBlock->getOffset()); // Instructions in a basic block that follow a terminator instruction // will never be executed because control flow will never reach them. @@ -101,7 +114,8 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Resetting the terminator flag terminatorEncoded = false; - std::printf("%.4u : now working on block %p offset %u\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset()); + if (traces_enabled) + std::printf("%.4u : now working on block %p offset %u\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset()); } } @@ -110,7 +124,9 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Skipping dead code if (terminatorEncoded) { // TODO In case of dead branches erase the target blocks - std::printf("%.4u : skipping dead code\n", currentBytePointer); + if (traces_enabled) + std::printf("%.4u : skipping dead code\n", currentBytePointer); + continue; } else { currentBasicBlock->append(instruction); @@ -123,7 +139,9 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { } if (instruction.isTerminator()) { - std::printf("%.4u : terminator encoded\n", currentBytePointer); + if (traces_enabled) + std::printf("%.4u : terminator encoded\n", currentBytePointer); + terminatorEncoded = true; } @@ -132,13 +150,16 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(instruction.getExtra()); if (iTargetBlock != m_offsetToBasicBlock.end()) { iTargetBlock->second->getReferers().insert(currentBasicBlock); - std::printf("%.4u : block reference %p (%u) ->? %p (%u)\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + + if (traces_enabled) + std::printf("%.4u : block reference %p (%u) ->? %p (%u)\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); } else assert(false); } } - std::printf("Phase 3. Wiping out chains of unreachable blocks\n"); + if (traces_enabled) + std::printf("Phase 3. Wiping out chains of unreachable blocks\n"); // At this stage all possible branches are encoded and block relations are registered in the referer sets. // We may now iterate through the blocks and wipe out unreachable ones. We need to iterate several times @@ -153,7 +174,8 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { BasicBlock* const block = *iBlock; if (block->getReferers().empty() && block->getOffset() != startOffset) { - std::printf("block %p (%u) is not reachable, erasing and clearing references\n", block, block->getOffset()); + if (traces_enabled) + std::printf("block %p (%u) is not reachable, erasing and clearing references\n", block, block->getOffset()); TSmalltalkInstruction terminator(opcode::extended); if (block->getTerminator(terminator) && terminator.isBranch()) { @@ -193,7 +215,9 @@ void ParsedBytecode::eraseReferer(uint16_t targetOffset, BasicBlock* referer) { if (iTargetBlock != m_offsetToBasicBlock.end()) { BasicBlock* const target = iTargetBlock->second; - std::printf("erasing reference %p (%u) -> %p (%u)\n", referer, referer->getOffset(), target, target->getOffset()); + if (traces_enabled) + std::printf("erasing reference %p (%u) -> %p (%u)\n", referer, referer->getOffset(), target, target->getOffset()); + target->getReferers().erase(referer); } else { assert(false); @@ -221,7 +245,8 @@ BasicBlock* ParsedBytecode::createBasicBlock(uint16_t blockOffset) { m_offsetToBasicBlock[blockOffset] = newBasicBlock; m_basicBlocks.push_back(newBasicBlock); -// std::printf("created new basic block %p (%u)\n", newBasicBlock, newBasicBlock->getOffset()); + if (traces_enabled) + std::printf("created new basic block %p (%u)\n", newBasicBlock, newBasicBlock->getOffset()); return newBasicBlock; } @@ -234,7 +259,9 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* // Unconditional branch case const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); if (iTargetBlock != m_offsetToBasicBlock.end()) { - std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + if (traces_enabled) + std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + iTargetBlock->second->getReferers().insert(currentBasicBlock); } else { assert(false); @@ -244,16 +271,20 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* // Terminator is one of conditional branch instructions. // We need to refer both of branch targets here. assert(terminator.getArgument() == special::branchIfTrue - || terminator.getArgument() == special::branchIfFalse); + || terminator.getArgument() == special::branchIfFalse); + + if (traces_enabled) + std::printf("%.4u : block reference %p (%u) ->F %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); // Case when branch condition is not met - std::printf("%.4u : block reference %p (%u) ->F %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); nextBlock->getReferers().insert(currentBasicBlock); // Case when branch condition is met const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); if (iTargetBlock != m_offsetToBasicBlock.end()) { - std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + if (traces_enabled) + std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + iTargetBlock->second->getReferers().insert(currentBasicBlock); } else { assert(false); @@ -265,6 +296,7 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* currentBasicBlock->append(TSmalltalkInstruction(opcode::doSpecial, special::branch, decoder.getBytePointer())); nextBlock->getReferers().insert(currentBasicBlock); - std::printf("%.4u : linking blocks %p (%u) -> %p (%u) with branch instruction\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); + if (traces_enabled) + std::printf("%.4u : linking blocks %p (%u) -> %p (%u) with branch instruction\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); } } \ No newline at end of file From 442d8cb581b833eed59ac2afc289200a226c5f87 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 22 Feb 2015 12:37:01 +0600 Subject: [PATCH 126/290] Fixes generation of phi nodes, eliminates use of m_targetToBlockMap Issue: #32 --- include/instructions.h | 16 +++++++- include/jit.h | 2 +- src/ControlGraph.cpp | 8 +++- src/MethodCompiler.cpp | 90 ++++++++++++++++++++++++------------------ 4 files changed, 75 insertions(+), 41 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index 22b1eb9..af2c63c 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -11,6 +11,10 @@ #include #include +namespace llvm { + class BasicBlock; +} + namespace st { struct TSmalltalkInstruction { @@ -165,12 +169,22 @@ class BasicBlock { return false; } - BasicBlock(uint16_t blockOffset = 0) : m_offset(blockOffset) { } + void setValue(llvm::BasicBlock* value) { m_value = value; } + llvm::BasicBlock* getValue() const { return m_value; } + + void setEndValue(llvm::BasicBlock* value) { m_value = value; } + llvm::BasicBlock* getEndValue() const { return m_value; } + + BasicBlock(uint16_t blockOffset = 0) + : m_offset(blockOffset), m_value(0), m_endValue(0) { } private: uint16_t m_offset; TInstructionVector m_instructions; TBasicBlockSet m_referers; + + llvm::BasicBlock* m_value; + llvm::BasicBlock* m_endValue; }; // This is a base class for ParsedMethod and ParsedBlock diff --git a/include/jit.h b/include/jit.h index 1f8c183..51a3dfa 100644 --- a/include/jit.h +++ b/include/jit.h @@ -232,7 +232,7 @@ class MethodCompiler { private: llvm::Module* m_JITModule; - std::map m_targetToBlockMap; +// std::map m_targetToBlockMap; void scanForBranches(TJITContext& jit, st::ParsedBytecode* source, uint32_t byteCount = 0); bool scanForBlockReturn(TJITContext& jit, uint32_t byteCount = 0); diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index e401870..8012448 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -378,7 +378,13 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar // In case of exactly one referer we may link values directly // Otherwise we should iterate through all referers and aggregate values using phi node const bool singleReferer = (refererBlocks.size() == 1); - ControlNode* result = singleReferer ? 0 : m_graph->newNode(); + ControlNode* result = 0; + + if (!singleReferer) { + PhiNode* const phi = m_graph->newNode(); + phi->setDomain(domain); + result = phi; + } BasicBlock::TBasicBlockSet::iterator iBlock = refererBlocks.begin(); for (; iBlock != refererBlocks.end(); ++iBlock) { diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 2d7ab11..e14beeb 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -202,7 +202,9 @@ void MethodCompiler::scanForBranches(TJITContext& jit, st::ParsedBytecode* sourc m_jit.function // method's function ); - compiler->m_targetToBlockMap[basicBlock.getOffset()] = newBlock; +// compiler->m_targetToBlockMap[basicBlock.getOffset()] = newBlock; + basicBlock.setValue(newBlock); + return true; } @@ -278,7 +280,8 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF scanForBranches(jit, jit.parsedMethod); // Switching builder context to the first basic block from the preamble - BasicBlock* body = m_targetToBlockMap[0]; + BasicBlock* const body = jit.parsedMethod->getBasicBlockByOffset(0)->getValue(); // m_targetToBlockMap[0]; + assert(body); body->setName("offset0"); jit.builder->SetInsertPoint(jit.preamble); @@ -292,7 +295,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Cleaning up m_blockFunctions.clear(); - m_targetToBlockMap.clear(); +// m_targetToBlockMap.clear(); return jit.function; } @@ -305,7 +308,8 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) private: virtual bool visitDomain(st::ControlDomain& domain) { - llvm::BasicBlock* newBlock = m_jit.compiler->m_targetToBlockMap[domain.getBasicBlock()->getOffset()]; + llvm::BasicBlock* newBlock = domain.getBasicBlock()->getValue(); + // llvm::BasicBlock* newBlock = m_jit.compiler->m_targetToBlockMap[domain.getBasicBlock()->getOffset()]; newBlock->moveAfter(m_jit.builder->GetInsertBlock()); // for a pretty sequenced BB output m_jit.builder->SetInsertPoint(newBlock, newBlock->getFirstInsertionPt()); @@ -554,7 +558,8 @@ void MethodCompiler::doPushBlock(TJITContext& jit) ss.str(""); ss << "offset" << blockOffset; - BasicBlock* blockBody = m_targetToBlockMap[blockOffset]; + BasicBlock* const blockBody = parsedBlock->getBasicBlockByOffset(blockOffset)->getValue(); // m_targetToBlockMap[blockOffset]; + assert(blockBody); blockBody->setName(ss.str()); blockContext.builder->CreateBr(blockBody); @@ -698,11 +703,22 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv } else if (st::PhiNode* const phiNode = node->cast()) { // Storing insertion point to return to it later - BasicBlock* const insertBlock = jit.builder->GetInsertBlock(); - const BasicBlock::iterator storedInsertionPoint = jit.builder->GetInsertPoint(); + BasicBlock* const storedInsertBlock = insertBlock ? insertBlock : jit.builder->GetInsertBlock(); + BasicBlock::iterator storedInsertionPoint = jit.builder->GetInsertPoint(); + + // If insert block is provided we need to store specified position, not just the last one + if (insertBlock) { + TerminatorInst* const terminator = insertBlock->getTerminator(); + if (terminator) + storedInsertionPoint = terminator; // inserting before terminator + else + storedInsertionPoint = insertBlock->end(); // appending to the end of the block + } // Switching to the phi insertion point - jit.builder->SetInsertPoint(insertBlock, insertBlock->getFirstInsertionPt()); + BasicBlock* const phiInsertBlock = phiNode->getDomain()->getBasicBlock()->getValue(); + assert(phiInsertBlock); + jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), inEdgesCount, "phi."); @@ -710,10 +726,10 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv for (std::size_t index = 0; index < incomingList.size(); index++) { const st::PhiNode::TIncoming& incoming = incomingList[index]; - BasicBlock* const inBlock = m_targetToBlockMap[incoming.domain->getBasicBlock()->getOffset()]; + BasicBlock* const inBlock = incoming.domain->getBasicBlock()->getEndValue(); // m_targetToBlockMap[incoming.domain->getBasicBlock()->getOffset()]; assert(inBlock); - // This call may change the insertion point of one of the incoming values is a value holder, + // This call may change the insertion point if one of the incoming values is a value holder, // not just a simple value. Load should be inserted in the incoming basic block. phiValue->addIncoming(getNodeValue(jit, incoming.node, inBlock), inBlock); } @@ -721,11 +737,11 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv // Phi is created at the top of the basic block but consumed later. // Value will be broken if GC will be invoked between phi and consumer. // Resetting the insertion point which may be changed in getNodeValue() before. - jit.builder->SetInsertPoint(insertBlock, insertBlock->getFirstInsertionPt()); + jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); Value* const phiHolder = protectPointer(jit, phiValue); // Finally, restoring original insertion point and loading the value from phi - jit.builder->SetInsertPoint(insertBlock, storedInsertionPoint); + jit.builder->SetInsertPoint(storedInsertBlock, storedInsertionPoint); value = jit.builder->CreateLoad(phiHolder); } @@ -767,6 +783,8 @@ void MethodCompiler::doSendBinary(TJITContext& jit) BasicBlock* const sendBinaryBlock = BasicBlock::Create(m_JITModule->getContext(), "asObjects.", jit.function); BasicBlock* const resultBlock = BasicBlock::Create(m_JITModule->getContext(), "result.", jit.function); + jit.currentNode->getDomain()->getBasicBlock()->setEndValue(resultBlock); + // Depending on the contents we may either do the integer operations // directly or create a send message call using operand objects jit.builder->CreateCondBr(isSmallInts, integersBlock, sendBinaryBlock); @@ -891,6 +909,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) if (jit.methodHasBlockReturn) { // Creating basic block that will be branched to on normal invoke BasicBlock* const nextBlock = BasicBlock::Create(m_JITModule->getContext(), "next.", jit.function); + jit.currentNode->getDomain()->getBasicBlock()->setEndValue(nextBlock); // Performing a function invoke result = jit.builder->CreateInvoke(m_runtimeAPI.sendMessage, nextBlock, jit.exceptionLandingPad, sendMessageArgs); @@ -959,13 +978,12 @@ void MethodCompiler::doSpecial(TJITContext& jit) // Loading branch target bytecode offset const uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); -// if (!iPreviousInst->isTerminator()) - { - // Finding appropriate branch target - // from the previously stored basic blocks - BasicBlock* const target = m_targetToBlockMap[targetOffset]; - jit.builder->CreateBr(target); - } + // Finding appropriate branch target + // from the previously stored basic blocks + BasicBlock* const target = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(targetOffset)->getValue(); // m_targetToBlockMap[targetOffset]; + assert(target); + + jit.builder->CreateBr(target); } break; case special::branchIfTrue: @@ -974,25 +992,20 @@ void MethodCompiler::doSpecial(TJITContext& jit) const uint32_t targetOffset = jit.currentNode->getInstruction().getExtra(); const uint32_t skipOffset = getSkipOffset(jit.currentNode); -// if (!iPreviousInst->isTerminator()) - { - // Finding appropriate branch target - // from the previously stored basic blocks - BasicBlock* const targetBlock = m_targetToBlockMap[targetOffset]; - - // This is a block that goes right after the branch instruction. - // If branch condition is not met execution continues right after - BasicBlock* const skipBlock = m_targetToBlockMap[skipOffset]; - - // Creating condition check - Value* const boolObject = (opcode == special::branchIfTrue) ? m_globals.trueObject : m_globals.falseObject; - Value* const condition = getArgument(jit); // jit.popValue(); - Value* const boolValue = jit.builder->CreateICmpEQ(condition, boolObject); - jit.builder->CreateCondBr(boolValue, targetBlock, skipBlock); - - // Switching to a newly created block - //jit.builder->SetInsertPoint(skipBlock); - } + // Finding appropriate branch target + // from the previously stored basic blocks + BasicBlock* const targetBlock = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(targetOffset)->getValue(); // m_targetToBlockMap[targetOffset]; + + // This is a block that goes right after the branch instruction. + // If branch condition is not met execution continues right after + BasicBlock* const skipBlock = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(skipOffset)->getValue(); // m_targetToBlockMap[skipOffset]; + + // Creating condition check + Value* const boolObject = (opcode == special::branchIfTrue) ? m_globals.trueObject : m_globals.falseObject; + Value* const condition = getArgument(jit); // jit.popValue(); + Value* const boolValue = jit.builder->CreateICmpEQ(condition, boolObject); + jit.builder->CreateCondBr(boolValue, targetBlock, skipBlock); + } break; case special::sendToSuper: { @@ -1068,6 +1081,7 @@ void MethodCompiler::doPrimitive(TJITContext& jit) // 2) bind primitiveFailed with any i1 result BasicBlock* const primitiveSucceededBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveSucceededBB", jit.function); BasicBlock* const primitiveFailedBB = BasicBlock::Create(m_JITModule->getContext(), "primitiveFailedBB", jit.function); + // FIXME setEndValue() ? compilePrimitive(jit, opcode, primitiveResult, primitiveFailed, primitiveSucceededBB, primitiveFailedBB); From 199d5fbd0b4d6b02698068a4b8bb5ebba7652388 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 22 Feb 2015 12:37:25 +0600 Subject: [PATCH 127/290] Fixes visualization of phi nodes Issue: #32 --- src/ControlGraphVisualizer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 06e2460..0a616ad 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -16,8 +16,8 @@ std::string edgeStyle(st::ControlNode* from, st::ControlNode* to) { const st::InstructionNode* const fromInstruction = from->cast(); const st::InstructionNode* const toInstruction = to->cast(); -// if (from->getNodeType() == st::ControlNode::ntPhi) -// return "[color=\"black\"]"; + if (from->getNodeType() == st::ControlNode::ntPhi && to->getNodeType() == st::ControlNode::ntPhi) + return "[style=invis color=red constraint=false]"; if (fromInstruction && fromInstruction->getInstruction().isBranch()) return "[color=\"grey\" style=\"dashed\"]"; @@ -62,6 +62,10 @@ bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { m_stream << " labelfloat=true color=\"blue\" fontcolor=\"blue\" style=\"dashed\" constraint=false];\n"; } } else if (const st::PhiNode* const phi = node.cast()) { + + m_stream << "\t\t" << phi->getIndex() << " -> " << phi->getDomain()->getEntryPoint()->getIndex() << " [" + << "labelfloat=true color=\"blue\" fontcolor=\"blue\" style=\"invis\" constraint=true ];\n"; + const st::PhiNode::TIncomingList& incomingList = phi->getIncomingList(); for (std::size_t index = 0; index < incomingList.size(); index++) { const st::PhiNode::TIncoming& incoming = incomingList[index]; From ef22b26277d004af387c509b2553497623fc694d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 22 Feb 2015 12:55:27 +0600 Subject: [PATCH 128/290] Fixes st::BasicBlock::get/setEndValue Issue: #32 --- include/instructions.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index af2c63c..9a2b138 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -172,8 +172,8 @@ class BasicBlock { void setValue(llvm::BasicBlock* value) { m_value = value; } llvm::BasicBlock* getValue() const { return m_value; } - void setEndValue(llvm::BasicBlock* value) { m_value = value; } - llvm::BasicBlock* getEndValue() const { return m_value; } + void setEndValue(llvm::BasicBlock* value) { m_endValue = value; } + llvm::BasicBlock* getEndValue() const { return m_endValue; } BasicBlock(uint16_t blockOffset = 0) : m_offset(blockOffset), m_value(0), m_endValue(0) { } From 694d434cb8f20195fc655f5396dc46975f5c8228 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 22 Feb 2015 21:20:11 +0600 Subject: [PATCH 129/290] Phi nodes are now encoded in a separate pass Encoding of phi nodes result in a complex code modification in several basic blocks. If some of basic blocks will not be finished at the time of phi encoding, malformed code will be produced. That's why we encode phi nodes at the very end of method processing. Dummy values are used to bind phi nodes to the instructions requesting phi as an argument. Issue: #32 --- include/jit.h | 5 ++ src/MethodCompiler.cpp | 121 ++++++++++++++++++++++++++++------------- 2 files changed, 87 insertions(+), 39 deletions(-) diff --git a/include/jit.h b/include/jit.h index 51a3dfa..4239e58 100644 --- a/include/jit.h +++ b/include/jit.h @@ -171,6 +171,10 @@ class MethodCompiler { st::ControlGraph* controlGraph; st::InstructionNode* currentNode; + // List of phi nodes waiting to be processed + typedef std::list TPhiList; + TPhiList pendingPhiNodes; + TMethod* originMethod; // Smalltalk method we're currently processing llvm::Function* function; // LLVM function that is created based on method @@ -246,6 +250,7 @@ class MethodCompiler { llvm::Value* processLeafNode(st::InstructionNode* instruction); llvm::Value* getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock = 0); + llvm::Value* getPhiValue(TJITContext& jit, st::PhiNode* phi); void setNodeValue(st::ControlNode* node, llvm::Value* value); llvm::Value* getArgument(TJITContext& jit, std::size_t index = 0); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index e14beeb..a2dbcba 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -325,6 +325,38 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) return NodeVisitor::visitNode(node); } + virtual void nodesVisited() { + BasicBlock* const lastBlock = m_jit.builder->GetInsertBlock(); + m_jit.currentNode->getDomain()->getBasicBlock()->setEndValue(lastBlock); + } + + virtual void domainsVisited() { + // Process pending phi nodes by replacing stub values with emitted code + + TJITContext::TPhiList::iterator iPhi = m_jit.pendingPhiNodes.begin(); + for (; iPhi != m_jit.pendingPhiNodes.end(); ++iPhi) { + st::PhiNode* const phi = *iPhi; + + Value* const stubValue = phi->getValue(); + assert(isa(stubValue)); + + // Emitting code of the real phi instruction and corresponding loads + Value* const realValueHolder = m_jit.compiler->getPhiValue(m_jit, phi); + + // Iterating through all value users and replacing stub uses with real value + for (Value::use_iterator iUse = stubValue->use_begin(); iUse != stubValue->use_end(); ++iUse) { + // Inserting the load directly before the value consumer + m_jit.builder->SetInsertPoint(iUse.getUse()); + Value* const realValue = m_jit.builder->CreateLoad(realValueHolder); + + iUse->replaceUsesOfWith(stubValue, realValue); + } + + phi->setValue(realValueHolder); + } + } + + private: TJITContext& m_jit; }; @@ -665,7 +697,23 @@ llvm::Value* MethodCompiler::processLeafNode(st::InstructionNode* instruction) } llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = 0*/) { - return getNodeValue(jit, jit.currentNode->getArgument(index)); + st::ControlNode* const argument = jit.currentNode->getArgument(index); + + if (st::PhiNode* const phi = argument->cast()) { + // Phi encoding generates additional load logic in potentially uncomplete basic blocks. + // We could not process encode phi node before all basic blocks are completed. + + // Creating dummy value that will be replaced later with actual value. + Value* const phiValue = UndefValue::get(jit.builder->getVoidTy()); + phi->setValue(phiValue); + + // Phi node processing will be done after last graph node is encoded. + jit.pendingPhiNodes.push_back(phi); + + return phiValue; + } + + return getNodeValue(jit, argument); } Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock /*= 0*/) @@ -702,53 +750,48 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv value = UndefValue::get(jit.builder->getVoidTy()); } else if (st::PhiNode* const phiNode = node->cast()) { - // Storing insertion point to return to it later - BasicBlock* const storedInsertBlock = insertBlock ? insertBlock : jit.builder->GetInsertBlock(); - BasicBlock::iterator storedInsertionPoint = jit.builder->GetInsertPoint(); - - // If insert block is provided we need to store specified position, not just the last one - if (insertBlock) { - TerminatorInst* const terminator = insertBlock->getTerminator(); - if (terminator) - storedInsertionPoint = terminator; // inserting before terminator - else - storedInsertionPoint = insertBlock->end(); // appending to the end of the block - } + // This should be done in a separate pass after encoding of all basic blocks + assert(insertBlock); + + Value* const phiHolder = getPhiValue(jit, phiNode); + + jit.builder->SetInsertPoint(insertBlock, insertBlock->getTerminator()); + value = jit.builder->CreateLoad(phiHolder); + } - // Switching to the phi insertion point - BasicBlock* const phiInsertBlock = phiNode->getDomain()->getBasicBlock()->getValue(); - assert(phiInsertBlock); - jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); + assert(value); + node->setValue(value); - PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), inEdgesCount, "phi."); + return value; +} - const st::PhiNode::TIncomingList& incomingList = phiNode->getIncomingList(); - for (std::size_t index = 0; index < incomingList.size(); index++) { - const st::PhiNode::TIncoming& incoming = incomingList[index]; +llvm::Value* MethodCompiler::getPhiValue(TJITContext& jit, st::PhiNode* phiNode) +{ + // Switching to the phi insertion point + BasicBlock* const phiInsertBlock = phiNode->getDomain()->getBasicBlock()->getValue(); + assert(phiInsertBlock); + jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); - BasicBlock* const inBlock = incoming.domain->getBasicBlock()->getEndValue(); // m_targetToBlockMap[incoming.domain->getBasicBlock()->getOffset()]; - assert(inBlock); + PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), phiNode->getIncomingList().size(), "phi."); - // This call may change the insertion point if one of the incoming values is a value holder, - // not just a simple value. Load should be inserted in the incoming basic block. - phiValue->addIncoming(getNodeValue(jit, incoming.node, inBlock), inBlock); - } + const st::PhiNode::TIncomingList& incomingList = phiNode->getIncomingList(); + for (std::size_t index = 0; index < incomingList.size(); index++) { + const st::PhiNode::TIncoming& incoming = incomingList[index]; - // Phi is created at the top of the basic block but consumed later. - // Value will be broken if GC will be invoked between phi and consumer. - // Resetting the insertion point which may be changed in getNodeValue() before. - jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); - Value* const phiHolder = protectPointer(jit, phiValue); + BasicBlock* const incomingBlock = incoming.domain->getBasicBlock()->getEndValue(); // m_targetToBlockMap[incoming.domain->getBasicBlock()->getOffset()]; + assert(incomingBlock); - // Finally, restoring original insertion point and loading the value from phi - jit.builder->SetInsertPoint(storedInsertBlock, storedInsertionPoint); - value = jit.builder->CreateLoad(phiHolder); + // This call may change the insertion point if one of the incoming values is a value holder, + // not just a simple value. Load should be inserted in the incoming basic block. + phiValue->addIncoming(getNodeValue(jit, incoming.node, incomingBlock), incomingBlock); } - assert(value); - node->setValue(value); + // Phi is created at the top of the basic block but consumed later. + // Value will be broken if GC will be invoked between phi and consumer. + // Resetting the insertion point which may be changed in getNodeValue() before. + jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); - return value; + return protectPointer(jit, phiValue); } void MethodCompiler::setNodeValue(st::ControlNode* node, llvm::Value* value) @@ -758,7 +801,7 @@ void MethodCompiler::setNodeValue(st::ControlNode* node, llvm::Value* value) Value* const oldValue = node->getValue(); if (oldValue) { if (isa(oldValue)) - oldValue->replaceAllUsesWith(value); + oldValue->replaceAllUsesWith(value); // FIXME Rewrite using direct access to use list else assert(false); } From e88889b1374dda315e4025d53d8d9392313a1367 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 23 Feb 2015 14:37:57 +0600 Subject: [PATCH 130/290] Adds stub replacement logic that deals with value holders Issue: #32 --- include/jit.h | 5 ++- src/MethodCompiler.cpp | 93 +++++++++++++++++++++--------------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/include/jit.h b/include/jit.h index 4239e58..a706c8b 100644 --- a/include/jit.h +++ b/include/jit.h @@ -248,10 +248,11 @@ class MethodCompiler { TExceptionAPI m_exceptionAPI; TBaseFunctions m_baseFunctions; - llvm::Value* processLeafNode(st::InstructionNode* instruction); + void replaceStub(TJITContext& jit, llvm::Value* stubValue, llvm::Value* realValue); + llvm::Value* getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock = 0); llvm::Value* getPhiValue(TJITContext& jit, st::PhiNode* phi); - void setNodeValue(st::ControlNode* node, llvm::Value* value); + void setNodeValue(TJITContext& jit, st::ControlNode* node, llvm::Value* value); llvm::Value* getArgument(TJITContext& jit, std::size_t index = 0); llvm::Value* allocateRoot(TJITContext& jit, llvm::Type* type); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index a2dbcba..7396707 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -341,18 +341,10 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) assert(isa(stubValue)); // Emitting code of the real phi instruction and corresponding loads - Value* const realValueHolder = m_jit.compiler->getPhiValue(m_jit, phi); + Value* const realValue = m_jit.compiler->getPhiValue(m_jit, phi); - // Iterating through all value users and replacing stub uses with real value - for (Value::use_iterator iUse = stubValue->use_begin(); iUse != stubValue->use_end(); ++iUse) { - // Inserting the load directly before the value consumer - m_jit.builder->SetInsertPoint(iUse.getUse()); - Value* const realValue = m_jit.builder->CreateLoad(realValueHolder); - - iUse->replaceUsesOfWith(stubValue, realValue); - } - - phi->setValue(realValueHolder); + m_jit.compiler->replaceStub(m_jit, stubValue, realValue); + phi->setValue(realValue); } } @@ -364,6 +356,27 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) visitor.run(); } +void MethodCompiler::replaceStub(TJITContext& jit, llvm::Value* stubValue, llvm::Value* realValue) +{ + assert(isa(stubValue)); + + // Iterating through all value users and replacing stub uses with real value + for (Value::use_iterator iUse = stubValue->use_begin(); iUse != stubValue->use_end(); ++iUse) { + // Replacement object may be either value or it's holder. + // In case of a holder we need to emit the load instruction. + const bool isHolder = isa(realValue); + Value* replacement = realValue; + + if (isHolder) { + // Inserting the load directly before the value consumer + jit.builder->SetInsertPoint(iUse.getUse()); + replacement = jit.builder->CreateLoad(realValue); + } + + iUse->replaceUsesOfWith(stubValue, replacement); + } +} + void MethodCompiler::writeInstruction(TJITContext& jit) { switch (jit.currentNode->getInstruction().getOpcode()) { // TODO Boundary checks against container's real size @@ -446,7 +459,7 @@ void MethodCompiler::doPushInstance(TJITContext& jit) field->setName(ss.str()); Value* const holder = protectPointer(jit, field); - setNodeValue(jit.currentNode, holder); + setNodeValue(jit, jit.currentNode, holder); } void MethodCompiler::doPushArgument(TJITContext& jit) @@ -473,7 +486,7 @@ void MethodCompiler::doPushArgument(TJITContext& jit) argument->setName(ss.str()); Value* const holder = protectPointer(jit, argument); - setNodeValue(jit.currentNode, holder); + setNodeValue(jit, jit.currentNode, holder); } void MethodCompiler::doPushTemporary(TJITContext& jit) @@ -492,7 +505,7 @@ void MethodCompiler::doPushTemporary(TJITContext& jit) temporary->setName(ss.str()); Value* const holder = protectPointer(jit, temporary); - setNodeValue(jit.currentNode, holder); + setNodeValue(jit, jit.currentNode, holder); } void MethodCompiler::doPushLiteral(TJITContext& jit) @@ -508,7 +521,7 @@ void MethodCompiler::doPushLiteral(TJITContext& jit) literal->setName(ss.str()); Value* const holder = protectPointer(jit, literal); - setNodeValue(jit.currentNode, holder); + setNodeValue(jit, jit.currentNode, holder); } void MethodCompiler::doPushConstant(TJITContext& jit) @@ -543,7 +556,7 @@ void MethodCompiler::doPushConstant(TJITContext& jit) std::fprintf(stderr, "JIT: unknown push constant %d\n", constant); } - setNodeValue(jit.currentNode, constantValue); + setNodeValue(jit, jit.currentNode, constantValue); } void MethodCompiler::doPushBlock(TJITContext& jit) @@ -602,7 +615,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) outs() << *blockContext.function << "\n"; // Running optimization passes on a block function - //JITRuntime::Instance()->optimizeFunction(blockContext.function); + JITRuntime::Instance()->optimizeFunction(blockContext.function); } // Create block object and fill it with context information @@ -616,7 +629,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) blockObject->setName("block."); Value* blockHolder = protectPointer(jit, blockObject); - setNodeValue(jit.currentNode, blockHolder); + setNodeValue(jit, jit.currentNode, blockHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, blockHolder)); } @@ -667,7 +680,7 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) Value* const argsHolder = protectPointer(jit, argumentsArray); argsHolder->setName("pArgs."); - setNodeValue(jit.currentNode, argsHolder); + setNodeValue(jit, jit.currentNode, argsHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); } @@ -686,16 +699,10 @@ void MethodCompiler::doSendUnary(TJITContext& jit) // FIXME Do not protect the result object because it will always be the literal value Value* const result = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); - setNodeValue(jit.currentNode, result); + setNodeValue(jit, jit.currentNode, result); //jit.pushValue(result); } -llvm::Value* MethodCompiler::processLeafNode(st::InstructionNode* instruction) -{ - assert(false); - return 0; -} - llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = 0*/) { st::ControlNode* const argument = jit.currentNode->getArgument(index); @@ -739,15 +746,9 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv return value; } - const std::size_t inEdgesCount = node->getInEdges().size(); if (st::InstructionNode* const instruction = node->cast()) { - // If node is a leaf from the same domain we may encode it locally. - // If not, create a dummy value wich will be replaced by real value later. - - if (!inEdgesCount && instruction->getDomain() == jit.currentNode->getDomain()) - value = processLeafNode(instruction); // FIXME WTF? - else - value = UndefValue::get(jit.builder->getVoidTy()); + // Сreating a dummy value which will be replaced by a real value later. + value = UndefValue::get(jit.builder->getVoidTy()); } else if (st::PhiNode* const phiNode = node->cast()) { // This should be done in a separate pass after encoding of all basic blocks @@ -794,17 +795,13 @@ llvm::Value* MethodCompiler::getPhiValue(TJITContext& jit, st::PhiNode* phiNode) return protectPointer(jit, phiValue); } -void MethodCompiler::setNodeValue(st::ControlNode* node, llvm::Value* value) +void MethodCompiler::setNodeValue(TJITContext& jit, st::ControlNode* node, llvm::Value* value) { assert(value); Value* const oldValue = node->getValue(); - if (oldValue) { - if (isa(oldValue)) - oldValue->replaceAllUsesWith(value); // FIXME Rewrite using direct access to use list - else - assert(false); - } + if (oldValue) + replaceStub(jit, oldValue, value); node->setValue(value); } @@ -826,7 +823,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) BasicBlock* const sendBinaryBlock = BasicBlock::Create(m_JITModule->getContext(), "asObjects.", jit.function); BasicBlock* const resultBlock = BasicBlock::Create(m_JITModule->getContext(), "result.", jit.function); - jit.currentNode->getDomain()->getBasicBlock()->setEndValue(resultBlock); +// jit.currentNode->getDomain()->getBasicBlock()->setEndValue(resultBlock); // Depending on the contents we may either do the integer operations // directly or create a send message call using operand objects @@ -920,7 +917,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) phi->addIncoming(sendMessageResult, sendBinaryBlock); Value* const resultHolder = protectPointer(jit, phi); - setNodeValue(jit.currentNode, resultHolder); + setNodeValue(jit, jit.currentNode, resultHolder); //jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } @@ -965,7 +962,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) } Value* const resultHolder = protectPointer(jit, result); - setNodeValue(jit.currentNode, resultHolder); + setNodeValue(jit, jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } @@ -1010,7 +1007,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) // Duplicating the origin value in the dup node. // When dup consumers will be remapped to the real // value in the ControlGraph this will be redundant. - setNodeValue(jit.currentNode, getNodeValue(jit, jit.currentNode->getArgument())); + setNodeValue(jit, jit.currentNode, getNodeValue(jit, jit.currentNode->getArgument())); break; case special::popTop: @@ -1074,7 +1071,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) Value* const result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); Value* const resultHolder = protectPointer(jit, result); - setNodeValue(jit.currentNode, resultHolder); + setNodeValue(jit, jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } break; @@ -1135,7 +1132,9 @@ void MethodCompiler::doPrimitive(TJITContext& jit) jit.builder->SetInsertPoint(primitiveFailedBB); // FIXME Are we really allowed to use the value without holder? - setNodeValue(jit.currentNode, primitiveResult); + setNodeValue(jit, jit.currentNode, primitiveResult); +// jit.currentNode->getDomain()->getBasicBlock()->setEndValue(primitiveFailedBB); + // jit.pushValue(m_globals.nilObject); } From 3815643cb8b5f8320594b2faf51c80986ae99d3b Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 23 Feb 2015 18:17:40 +0600 Subject: [PATCH 131/290] Label of the branch now includes target offset Issue: #32 --- src/ControlGraphVisualizer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 0a616ad..5087b4a 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -115,6 +115,10 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { TSymbolArray* const literals = m_graph->getParsedMethod()->getOrigin()->literals; TSymbol* const name = literals->getField(instruction->getInstruction().getArgument()); label += " " + name->toString(); + } else if (instruction->getInstruction().isBranch()) { + std::stringstream ss; + ss << " " << instruction->getInstruction().getExtra(); + label += ss.str(); } const bool isTerminator = instruction->getInstruction().isTerminator(); From 0b2d5033354d1759c29ea49fe422c968346b86f7 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 23 Feb 2015 18:21:43 +0600 Subject: [PATCH 132/290] Adds field to st::PhiNode that holds llvm::PHINode* Typically, nodes in a CFG are represented by thier values. In case of a Phi node we need to store both, the value holder and the LLVM Phi node. Latter is used at the post processing stage to encode Phi's incoming list. Issue: #32 --- include/analysis.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index 9595005..db1cd39 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -8,6 +8,7 @@ namespace llvm { class Value; + class PHINode; } namespace st { @@ -204,7 +205,7 @@ class PushBlockNode : public InstructionNode { // to a node having a phi node as it's agrument. class PhiNode : public ControlNode { public: - PhiNode(uint32_t index) : ControlNode(index) { m_incomingList.reserve(2); } + PhiNode(uint32_t index) : ControlNode(index), m_phiValue(0) { m_incomingList.reserve(2); } virtual TNodeType getNodeType() const { return ntPhi; } uint32_t getPhiIndex() const { return m_phiIndex; } void setPhiIndex(uint32_t value) { m_phiIndex = value; } @@ -228,9 +229,13 @@ class PhiNode : public ControlNode { const TIncomingList& getIncomingList() const { return m_incomingList; } TNodeSet getRealValues(); + llvm::PHINode* getPhiValue() const { return m_phiValue; } + void setPhiValue(llvm::PHINode* value) { m_phiValue = value; } + private: - uint32_t m_phiIndex; - TIncomingList m_incomingList; + uint32_t m_phiIndex; + TIncomingList m_incomingList; + llvm::PHINode* m_phiValue; }; // Tau node is reserved for further use in type inference subsystem. From 06f26d63316fb084764b90bd248bbc3556aca336 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 23 Feb 2015 18:30:43 +0600 Subject: [PATCH 133/290] Refactors phi generation logic Phi nodes directly used as arguments are now encoded in two steps. First, phi instruction is created. It is protected by the holder and then returned to the value consumer. Also, phi is registered in the post processing list. Second, post processing is performed. On this step all registered phi's get their incoming list encoded. This may result in a recursive generation of phi nodes and loads from value holders (if any). No temporary values are used. Issue: #32 --- include/jit.h | 3 +- src/MethodCompiler.cpp | 111 ++++++++++++++++------------------------- 2 files changed, 45 insertions(+), 69 deletions(-) diff --git a/include/jit.h b/include/jit.h index a706c8b..24748a7 100644 --- a/include/jit.h +++ b/include/jit.h @@ -248,10 +248,9 @@ class MethodCompiler { TExceptionAPI m_exceptionAPI; TBaseFunctions m_baseFunctions; - void replaceStub(TJITContext& jit, llvm::Value* stubValue, llvm::Value* realValue); - llvm::Value* getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock = 0); llvm::Value* getPhiValue(TJITContext& jit, st::PhiNode* phi); + void encodePhiIncomings(TJITContext& jit, st::PhiNode* phiNode); void setNodeValue(TJITContext& jit, st::ControlNode* node, llvm::Value* value); llvm::Value* getArgument(TJITContext& jit, std::size_t index = 0); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 7396707..7e4dd04 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -331,20 +331,17 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) } virtual void domainsVisited() { - // Process pending phi nodes by replacing stub values with emitted code + // Encoding incoming values of pending phi nodes TJITContext::TPhiList::iterator iPhi = m_jit.pendingPhiNodes.begin(); for (; iPhi != m_jit.pendingPhiNodes.end(); ++iPhi) { st::PhiNode* const phi = *iPhi; - Value* const stubValue = phi->getValue(); - assert(isa(stubValue)); + // Phi should already be encoded, all we need is to fill the incoming list + assert(phi->getValue()); + assert(phi->getPhiValue()); - // Emitting code of the real phi instruction and corresponding loads - Value* const realValue = m_jit.compiler->getPhiValue(m_jit, phi); - - m_jit.compiler->replaceStub(m_jit, stubValue, realValue); - phi->setValue(realValue); + m_jit.compiler->encodePhiIncomings(m_jit, phi); } } @@ -356,27 +353,6 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) visitor.run(); } -void MethodCompiler::replaceStub(TJITContext& jit, llvm::Value* stubValue, llvm::Value* realValue) -{ - assert(isa(stubValue)); - - // Iterating through all value users and replacing stub uses with real value - for (Value::use_iterator iUse = stubValue->use_begin(); iUse != stubValue->use_end(); ++iUse) { - // Replacement object may be either value or it's holder. - // In case of a holder we need to emit the load instruction. - const bool isHolder = isa(realValue); - Value* replacement = realValue; - - if (isHolder) { - // Inserting the load directly before the value consumer - jit.builder->SetInsertPoint(iUse.getUse()); - replacement = jit.builder->CreateLoad(realValue); - } - - iUse->replaceUsesOfWith(stubValue, replacement); - } -} - void MethodCompiler::writeInstruction(TJITContext& jit) { switch (jit.currentNode->getInstruction().getOpcode()) { // TODO Boundary checks against container's real size @@ -704,23 +680,7 @@ void MethodCompiler::doSendUnary(TJITContext& jit) } llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = 0*/) { - st::ControlNode* const argument = jit.currentNode->getArgument(index); - - if (st::PhiNode* const phi = argument->cast()) { - // Phi encoding generates additional load logic in potentially uncomplete basic blocks. - // We could not process encode phi node before all basic blocks are completed. - - // Creating dummy value that will be replaced later with actual value. - Value* const phiValue = UndefValue::get(jit.builder->getVoidTy()); - phi->setValue(phiValue); - - // Phi node processing will be done after last graph node is encoded. - jit.pendingPhiNodes.push_back(phi); - - return phiValue; - } - - return getNodeValue(jit, argument); + return getNodeValue(jit, jit.currentNode->getArgument(index)); } Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock /*= 0*/) @@ -746,18 +706,33 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv return value; } - if (st::InstructionNode* const instruction = node->cast()) { - // Сreating a dummy value which will be replaced by a real value later. - value = UndefValue::get(jit.builder->getVoidTy()); + if (st::PhiNode* const phiNode = node->cast()) { + if (!insertBlock) { + // Storing original insert position to return to + BasicBlock* const currentBlock = jit.builder->GetInsertBlock(); + BasicBlock::iterator storedInsertPoint = jit.builder->GetInsertPoint(); - } else if (st::PhiNode* const phiNode = node->cast()) { - // This should be done in a separate pass after encoding of all basic blocks - assert(insertBlock); + // Endoing phi and it's holder + jit.builder->SetInsertPoint(currentBlock, currentBlock->getFirstInsertionPt()); + PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), phiNode->getIncomingList().size(), "phi."); + Value* const phiHolder = protectPointer(jit, phiValue); - Value* const phiHolder = getPhiValue(jit, phiNode); + phiNode->setPhiValue(phiValue); - jit.builder->SetInsertPoint(insertBlock, insertBlock->getTerminator()); - value = jit.builder->CreateLoad(phiHolder); + // Appending phi to the post processing list that will fill the incomings + jit.pendingPhiNodes.push_back(phiNode); + + // Restoring original insert position + jit.builder->SetInsertPoint(currentBlock, storedInsertPoint); + + // Encoding the value load to be used in requesting instruction + value = jit.builder->CreateLoad(phiHolder); + } else { + Value* const phiHolder = getPhiValue(jit, phiNode); + + jit.builder->SetInsertPoint(insertBlock, insertBlock->getTerminator()); + value = jit.builder->CreateLoad(phiHolder); + } } assert(value); @@ -774,7 +749,19 @@ llvm::Value* MethodCompiler::getPhiValue(TJITContext& jit, st::PhiNode* phiNode) jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), phiNode->getIncomingList().size(), "phi."); + phiNode->setPhiValue(phiValue); + encodePhiIncomings(jit, phiNode); + // Phi is created at the top of the basic block but consumed later. + // Value will be broken if GC will be invoked between phi and consumer. + // Resetting the insertion point which may be changed in getNodeValue() before. + jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); + + return protectPointer(jit, phiValue); +} + +void MethodCompiler::encodePhiIncomings(TJITContext& jit, st::PhiNode* phiNode) +{ const st::PhiNode::TIncomingList& incomingList = phiNode->getIncomingList(); for (std::size_t index = 0; index < incomingList.size(); index++) { const st::PhiNode::TIncoming& incoming = incomingList[index]; @@ -784,24 +771,14 @@ llvm::Value* MethodCompiler::getPhiValue(TJITContext& jit, st::PhiNode* phiNode) // This call may change the insertion point if one of the incoming values is a value holder, // not just a simple value. Load should be inserted in the incoming basic block. - phiValue->addIncoming(getNodeValue(jit, incoming.node, incomingBlock), incomingBlock); + phiNode->getPhiValue()->addIncoming(getNodeValue(jit, incoming.node, incomingBlock), incomingBlock); } - - // Phi is created at the top of the basic block but consumed later. - // Value will be broken if GC will be invoked between phi and consumer. - // Resetting the insertion point which may be changed in getNodeValue() before. - jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); - - return protectPointer(jit, phiValue); } void MethodCompiler::setNodeValue(TJITContext& jit, st::ControlNode* node, llvm::Value* value) { assert(value); - - Value* const oldValue = node->getValue(); - if (oldValue) - replaceStub(jit, oldValue, value); + assert(! node->getValue()); node->setValue(value); } From a2c47d7f96a1fb7397489957c98b3ee7c51bd1a7 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 24 Feb 2015 01:22:02 +0300 Subject: [PATCH 134/290] Fixes primitive startNewProcess Issue: #32 --- src/MethodCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 7e4dd04..a1169e5 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -1171,7 +1171,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, case primitive::startNewProcess: { // 6 // /* ticks. unused */ jit.popValue(); - Value* const processObject = getArgument(jit, 1); // jit.popValue(); + Value* const processObject = getArgument(jit, 0); // jit.popValue(); Value* const process = jit.builder->CreateBitCast(processObject, m_baseTypes.process->getPointerTo()); Function* const executeProcess = m_JITModule->getFunction("executeProcess"); From 17beb07008110107ab48094caec731ff9d988dce Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 24 Feb 2015 01:46:16 +0300 Subject: [PATCH 135/290] Fixes the node value of doPrimitive (it is always nil, because the flow continues in primitiveFailedBB) Issue: #32 --- src/MethodCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index a1169e5..5024956 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -1109,7 +1109,7 @@ void MethodCompiler::doPrimitive(TJITContext& jit) jit.builder->SetInsertPoint(primitiveFailedBB); // FIXME Are we really allowed to use the value without holder? - setNodeValue(jit, jit.currentNode, primitiveResult); + setNodeValue(jit, jit.currentNode, m_globals.nilObject); // jit.currentNode->getDomain()->getBasicBlock()->setEndValue(primitiveFailedBB); // jit.pushValue(m_globals.nilObject); From e141cc6b610b003ff210f161493167f06b956913 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 25 Feb 2015 22:59:24 +0600 Subject: [PATCH 136/290] Fixes encoding of dup instruction Issue: #32 --- src/MethodCompiler.cpp | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 5024956..dadff44 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -243,12 +243,12 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Creating the function named as "Class>>method" or using provided one jit.function = methodFunction ? methodFunction : createFunction(method); - { - std::ostringstream ss; - ss << "dots/" << jit.function->getName().data() << ".dot"; - ControlGraphVisualizer vis(jit.controlGraph, ss.str()); - vis.run(); - } +// { +// std::ostringstream ss; +// ss << "dots/" << jit.function->getName().data() << ".dot"; +// ControlGraphVisualizer vis(jit.controlGraph, ss.str()); +// vis.run(); +// } // Creating the preamble basic block and inserting it into the function // It will contain basic initialization code (args, temps and so on) @@ -548,12 +548,12 @@ void MethodCompiler::doPushBlock(TJITContext& jit) ss << jit.originMethod->klass->name->toString() + ">>" + jit.originMethod->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); - { - std::ostringstream ss; - ss << "dots/" << blockFunctionName << ".dot"; - ControlGraphVisualizer vis(blockContext.controlGraph, ss.str()); - vis.run(); - } +// { +// std::ostringstream ss; +// ss << "dots/" << blockFunctionName << ".dot"; +// ControlGraphVisualizer vis(blockContext.controlGraph, ss.str()); +// vis.run(); +// } std::vector blockParams; blockParams.push_back(m_baseTypes.block->getPointerTo()); // block object with context information @@ -588,10 +588,12 @@ void MethodCompiler::doPushBlock(TJITContext& jit) writeFunctionBody(blockContext); - outs() << *blockContext.function << "\n"; +// outs() << *blockContext.function << "\n"; // Running optimization passes on a block function JITRuntime::Instance()->optimizeFunction(blockContext.function); + +// outs() << *blockContext.function << "\n"; } // Create block object and fill it with context information @@ -980,12 +982,17 @@ void MethodCompiler::doSpecial(TJITContext& jit) } break; - case special::duplicate: + case special::duplicate: { // Duplicating the origin value in the dup node. // When dup consumers will be remapped to the real // value in the ControlGraph this will be redundant. - setNodeValue(jit, jit.currentNode, getNodeValue(jit, jit.currentNode->getArgument())); + + Value* const original = getNodeValue(jit, jit.currentNode->getArgument()); + Value* const copy = protectPointer(jit, original); + + setNodeValue(jit, jit.currentNode, copy); break; + } case special::popTop: // Simply doing nothing From ac61332a6227cebbe56d76c4c7c4e5d03f17e8f1 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 25 Feb 2015 22:59:51 +0600 Subject: [PATCH 137/290] Fixes image to conform in-image compiler --- image/imageSource.st | 179 +++++++++++++++++++++++++++++++------------ 1 file changed, 132 insertions(+), 47 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index 33bcf35..8dab527 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -322,7 +322,7 @@ hierarchy | class list | METHOD Class listMethods methods keysDo: - [ :name | name printNl ] + [ :key | key printNl ] ! METHOD Class allMethods | allMethods | @@ -836,7 +836,7 @@ METHOD LoopTest symbolStressTest |x| x <- MyArray new: 100. 100 times: [:idx| x at: idx put: (idx asString asSymbol) ]. "FIXME speedup" - [ x location: #0; ] assertEq: 0. + [ x location: #0 ] assertEq: 0. ! COMMENT ----------ClassTest-------- @@ -1014,13 +1014,13 @@ CLASS ContextTest Test METHOD ContextTest backtrace - [ [] method name ] assertEq: 'backtrace' withComment: '1'. - [ [] creatingContext method name ] assertEq: 'backtrace' withComment: '2'. + [ [nil] method name ] assertEq: 'backtrace' withComment: '1'. + [ [nil] creatingContext method name ] assertEq: 'backtrace' withComment: '2'. ! METHOD ContextTest instanceClass - [ [] creatingContext arguments at: 1 ; class printString ] assertEq: 'ContextTest'. + [ [nil] creatingContext arguments at: 1 ; class printString ] assertEq: 'ContextTest'. ! COMMENT -------PrimitiveTest---------- @@ -1688,22 +1688,88 @@ sortBenchmark: data ^data sort ! -METHOD Undefined +METHOD System runSortBenchmarkFor: data ^Timer millisecondsToRun: [ nil sortBenchmark: data ] ! +METHOD MetaSystem +rebuildImage | classes | + classes <- List new. + globals do: [ :global | + (global isKindOf: Class) + ifTrue: [ classes add: global ] ]. + + classes do: [ :class | class methods do: + [ :method | self rebuildMethod: method for: class ] ] +! + +METHOD MetaSystem +rebuildMethod: aMethod for: aClass | newMethod | + (aClass = Undefined) + ifTrue: [ + 'Compiling ' print. aClass print. ':' print. + aMethod name print. '... ' print. + ]. + + newMethod <- aClass parseMethod: aMethod text. + newMethod notNil + ifTrue: [ 'ok' printNl ] + ifFalse: [ ^nil ]. + + self assignClass: aClass to: newMethod. + aClass methods at: (aMethod name) put: newMethod. + Method flushCache. +! + +METHOD MetaSystem +assignClass: aClass to: aMethod + super in: aMethod at: 6 put: aClass. +! + +METHOD MetaSystem +getMethodClass: aMethod + ^super in: aMethod at: 6 +! + METHOD Undefined -main | command data | - " initialize classes " +stackOverflowTest |x| + 1 to: 100 do: [ 42 ] +! - Char initialize. +METHOD Undefined +runAllTests SmallIntTest new runAll. ArrayTest new runAll. PrimitiveTest new runAll. MagnitudeTest new runAll. ListTest new runAll. MethodLookupTest new runAll. +! + + +METHOD MetaSystem +fixMethodClasses | classes | + classes <- List new. + globals do: [ :global | + (global isKindOf: Class) + ifTrue: [ classes add: global ] ]. + + classes do: [ :class | class methods do: + [ :method | self assignClass: class to: method ] ] +! + +METHOD Undefined +main | command data x | + Char initialize. + "System fixMethodClasses." + + "SmallIntTest new runAll. + ArrayTest new runAll. + PrimitiveTest new runAll. + MagnitudeTest new runAll. + ListTest new runAll. + MethodLookupTest new runAll." "Scheduler initialize. Thread run: [ 1 to: 10 do: [ :x | 'A' printNl ] ]. @@ -1726,6 +1792,14 @@ main | command data | "1 to: 100 do: [ :x | Jit do: [ nil loopBenchmark ] ]." + "Jit do: [ List with: 42 with: 43; sort ]." + + "Jit do: [ nil runSort: 10000 ]." + "Jit do: [ 'hello' printNl ]." + "Jit do: [ System rebuildImage ]." + "Jit do: [ [ :p | x <- p ] value: 42 ]." + "Jit do: [ SmallIntTest new runAll ]." + [ command <- String readline: '->'. command notNil ] whileTrue: [ command isEmpty ifFalse: [ command doIt printNl ] ] @@ -2142,6 +2216,7 @@ temporarySize METHOD Method args: argNames inst: instNames temp: tempNames " Hook for recording symbolic debug " + ^self ! COMMENT -----------Chars-------------- METHOD MetaChar @@ -2258,7 +2333,9 @@ asString ! METHOD Char printString - ^ String new: 2; at: 1 put: $$ ; at: 2 put: self + ^ String new: 2; + at: 1 put: $$; + at: 2 put: self ! METHOD Char = aChar @@ -2809,14 +2886,14 @@ asSymbol ^ Symbol new: self ! METHOD String -doIt | meth command | +doIt | meth command | command <- 'doItCommand '. ((self words at: 1) at: 1) = $| ifFalse: [ command <- command + ' ^ ' ]. - meth <- Undefined parseMethod: command + self. - ^ meth notNil - ifTrue: [ ^ Context new - perform: meth withArguments: (Array new: 1) ] + meth <- Undefined parseMethod: command + self. + ^ meth notNil + ifTrue: [ ^ Context new + perform: meth withArguments: (Array new: 1) ] ! METHOD String basicAt: index @@ -3123,7 +3200,7 @@ keysDo: aBlock 1 to: keys size do: [:i| aBlock value: (keys at: i)] ! METHOD Dictionary -keysAsArray | i ret | +keysAsArray | ret | ret <- Array new: keys size. 1 to: keys size do: [:i| ret at: i put: (keys at: i)]. ^ ret @@ -4425,9 +4502,11 @@ lexInteger | start | METHOD Parser lexAlnum | cc start | start <- index. - [ ((cc <- self nextChar) isAlphanumeric) or: [ cc = $: ] ] - whileTrue: [ nil ]. - " add any trailing colons " + + [ cc <- self nextChar. cc isAlphanumeric or: [ cc = $: ] ] + whileTrue: [ nil ]. + + " add any trailing colons " token <- text from: start to: index - 1 ! METHOD Parser @@ -4539,16 +4618,19 @@ readStatement | lnum | ! METHOD Parser readExpression | node lnum | - self tokenIsName ifFalse: [ ^ self readCascade: self readTerm ]. - node <- self nameNode: token asSymbol. self nextLex. - self tokenIsArrow - ifTrue: [ node assignable - ifFalse: [ self error: 'illegal assignment']. - lnum <- lineNum. - self nextLex. - ^ (AssignNode at: lnum) target: - node expression: self readExpression ]. - ^ self readCascade: node + self tokenIsName ifFalse: [ ^ self readCascade: self readTerm ]. + node <- self nameNode: token asSymbol. self nextLex. + + self tokenIsArrow ifTrue: [ + node assignable ifFalse: [ self error: 'illegal assignment']. + + lnum <- lineNum. + self nextLex. + + ^ (AssignNode at: lnum) target: node expression: self readExpression + ]. + + ^ self readCascade: node ! METHOD Parser tokenIsArrow @@ -4574,23 +4656,24 @@ readTerm | node lnum | ! METHOD Parser nameNode: name - " make a new name node " - name == #super - ifTrue: [ ^ (ArgumentNode at: lineNum) position: 0 ]. - (1 to: tempNames size) do: [:i | - (name == (tempNames at: i)) - ifTrue: [ ^ (TemporaryNode at: lineNum) - position: i ] ]. - (1 to: argNames size) do: [:i | - (name == (argNames at: i)) - ifTrue: [ ^ (ArgumentNode at: lineNum) position: i ] ]. - (1 to: instNames size) do: [:i | - (name == (instNames at: i)) - ifTrue: [ ^ (InstNode at: lineNum) position: i ] ]. - ^ (LiteralNode at: lineNum); - value: (globals at: name - ifAbsent: [ ^ self error: - 'unrecognized name: ' + name printString ]) + " make a new name node " + name == #super + ifTrue: [ ^ (ArgumentNode at: lineNum) position: 0 ]. + (1 to: tempNames size) do: [:i | + (name == (tempNames at: i)) + ifTrue: [ ^ (TemporaryNode at: lineNum) + position: i ] ]. + (1 to: argNames size) do: [:i | + (name == (argNames at: i)) + ifTrue: [ ^ (ArgumentNode at: lineNum) position: i ] ]. + (1 to: instNames size) do: [:i | + (name == (instNames at: i)) + ifTrue: [ ^ (InstNode at: lineNum) position: i ] ]. + + ^ (LiteralNode at: lineNum); + value: (globals at: name + ifAbsent: [ ^ self error: + 'unrecognized name: ' + name printString ]) ! METHOD Parser readLiteral | node | @@ -4905,6 +4988,7 @@ compile: encoder block: inBlock | patchLocation | super compile: encoder. encoder genHigh: 12 low: temporaryLocation. patchLocation <- encoder genVal: 0. + self compileInLine: encoder block: true. encoder genHigh: 15 low: 2. " return top of stack " encoder patch: patchLocation @@ -5048,6 +5132,7 @@ name: n METHOD Encoder lineNum: l " Don't care, except in DebugEncoder subclass " + ^self ! METHOD Encoder pushArgs: n @@ -5329,7 +5414,7 @@ name ! METHOD MetaSystem collectGarbage - <254> + <254>. self primitiveFailed ! METHOD MetaSystem From daf37dfd13b8da2016c028d79f53f1bc46b75a6e Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 28 Feb 2015 15:20:38 +0600 Subject: [PATCH 138/290] Adds control graph walking routines Issue: #32 --- include/analysis.h | 109 ++++++++++++++++++++++++++++++++++++++++++- src/ControlGraph.cpp | 4 +- 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index db1cd39..ee9120d 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -74,12 +74,12 @@ typedef std::vector TNodeList; class NodeIndexCompare { public: - bool operator() (const ControlNode* a, const ControlNode* b); + bool operator() (const ControlNode* a, const ControlNode* b) const; }; class DomainOffsetCompare { public: - bool operator() (const ControlDomain* a, const ControlDomain* b); + bool operator() (const ControlDomain* a, const ControlDomain* b) const; }; typedef std::set TNodeSet; @@ -511,6 +511,111 @@ class PlainNodeVisitor { ControlGraph* const m_graph; }; +class GraphWalker { +public: + GraphWalker() { } + virtual ~GraphWalker() { } + + void addStopNode(ControlNode* node) { m_stopNodes.insert(node); } + void addStopNodes(const TNodeSet& nodes) { m_stopNodes.insert(nodes.begin(), nodes.end()); } + void resetStopNodes() { m_stopNodes.clear(); } + + enum TVisitResult { + vrKeepWalking = 0, + vrSkipPath, + vrStopWalk + }; + + virtual TVisitResult visitNode(ControlNode* node) = 0; + virtual void nodesVisited() { } + + enum TWalkDirection { + wdForward, + wdBackward + }; + + void run(ControlNode* startNode, TWalkDirection direction) { + assert(startNode); + m_direction = direction; + + walkIn(startNode); + nodesVisited(); + } + +private: + bool walkIn(ControlNode* currentNode) { + const TNodeSet& nodes = (m_direction == wdForward) ? + currentNode->getOutEdges() : currentNode->getInEdges(); + + for (TNodeSet::iterator iNode = nodes.begin(); iNode != nodes.end(); ++iNode) { + ControlNode* const node = *iNode; + + if (m_stopNodes.find(node) != m_stopNodes.end()) + continue; + else + m_stopNodes.insert(node); + + switch (const TVisitResult result = visitNode(node)) { + case vrKeepWalking: + if (!walkIn(node)) + return false; + break; + + case vrStopWalk: + return false; + + case vrSkipPath: + continue; + } + } + + return true; + } + +private: + TWalkDirection m_direction; + TNodeSet m_stopNodes; +}; + +class ForwardWalker : public GraphWalker { +public: + void run(ControlNode* startNode) { GraphWalker::run(startNode, wdForward); } +}; + +class PathVerifier : public ForwardWalker { +public: + PathVerifier(const TNodeSet& destinationNodes) + : m_destinationNodes(destinationNodes), m_verified(false) {} + + bool isVerified() const { return m_verified; } + void reset() { resetStopNodes(); m_verified = false; } + + void run(ControlNode* startNode) { + assert(startNode); + m_verified = false; + ForwardWalker::run(startNode); + } + +private: + virtual TVisitResult visitNode(ControlNode* node) { + // Checking if there is a path between + // start node and any of the destination nodes. + + if (m_destinationNodes.find(node) != m_destinationNodes.end()) { + m_verified = true; + return vrStopWalk; + } + + return vrKeepWalking; + } + +private: + const TNodeSet& m_destinationNodes; + bool m_verified; +}; + + + } // namespace st #endif diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 8012448..87f9c01 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -5,12 +5,12 @@ using namespace st; static const bool traces_enabled = false; -bool NodeIndexCompare::operator() (const ControlNode* a, const ControlNode* b) +bool NodeIndexCompare::operator() (const ControlNode* a, const ControlNode* b) const { return a->getIndex() < b->getIndex(); } -bool DomainOffsetCompare::operator() (const ControlDomain* a, const ControlDomain* b) { +bool DomainOffsetCompare::operator() (const ControlDomain* a, const ControlDomain* b) const { return a->getBasicBlock()->getOffset() < b->getBasicBlock()->getOffset(); } From fec3697876a495216967cfcae21ce1792288749f Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 28 Feb 2015 18:18:44 +0600 Subject: [PATCH 139/290] Adds TSmalltalkInstruction::mayCauseGC() Issue: #32 --- include/instructions.h | 1 + src/TSmalltalkInstruction.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/instructions.h b/include/instructions.h index 9a2b138..bf4be31 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -57,6 +57,7 @@ struct TSmalltalkInstruction { bool isBranch() const; bool isValueProvider() const; bool isValueConsumer() const; + bool mayCauseGC() const; std::string toString() const; diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index acef4e7..6a68f15 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -100,6 +100,35 @@ bool st::TSmalltalkInstruction::isTrivial() const { } } +bool st::TSmalltalkInstruction::mayCauseGC() const { + // NOTE We expect that markArguments is encoded + // directly, so no heap allocation occur + + if (isTrivial() || isTerminator()) + return false; + + switch (m_opcode) { + case opcode::assignTemporary: + case opcode::assignInstance: + return false; + + case opcode::sendUnary: + case opcode::sendBinary: + case opcode::sendMessage: + return true; + + case opcode::doSpecial: + // The only special that may cause GC + return m_argument == special::sendToSuper; + + case opcode::doPrimitive: + return true; + + default: + return false; + } +} + bool st::TSmalltalkInstruction::isValueConsumer() const { switch (m_opcode) { case opcode::pushInstance: From 42f5d7c10ac595b230baf917f6aa065ef04389a9 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 28 Feb 2015 18:19:50 +0600 Subject: [PATCH 140/290] Adds logic that answers a question whether produced value should be protected We should protect the value by holder if consumer of this value is far away. By far away we mean that it crosses the barrier of potential garbage collection. For example if value is consumed right next to the point it was produced, then protection is not neccessary. Otherwise if there are memory allocation points in between then value protection is a must. In order to find out whether value protection is required, we trace the possible control- and data-flow paths and check what instructions are on the way between value producer and all potential consumers. If there is at least one dangerous operation on any of possible execution paths then the whole value should be protected. Issue: #32 --- include/jit.h | 8 +++ src/MethodCompiler.cpp | 138 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/include/jit.h b/include/jit.h index 24748a7..2c5315a 100644 --- a/include/jit.h +++ b/include/jit.h @@ -166,6 +166,12 @@ struct TBaseFunctions { class MethodCompiler { public: + enum TProtectionMode { + pmUnknown = 0, + pmShouldProtect, + pmSafe + }; + struct TJITContext { st::ParsedMethod* parsedMethod; st::ControlGraph* controlGraph; @@ -256,6 +262,8 @@ class MethodCompiler { llvm::Value* allocateRoot(TJITContext& jit, llvm::Type* type); llvm::Value* protectPointer(TJITContext& jit, llvm::Value* value); + llvm::Value* protectProducerNode(TJITContext& jit, st::ControlNode* node, llvm::Value* value); + bool shouldProtectProducer(st::ControlNode* node); void writePreamble(TJITContext& jit, bool isBlock = false); void writeFunctionBody(TJITContext& jit); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index dadff44..96c7412 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -125,6 +125,144 @@ Value* MethodCompiler::protectPointer(TJITContext& jit, Value* value) return holder; } +Value* MethodCompiler::protectProducerNode(TJITContext& jit, st::ControlNode* node, Value* value) +{ + if (shouldProtectProducer(jit.currentNode)) + return protectPointer(jit, value); + else + return value; // return value as is +} + +class Detector { +public: + Detector(st::PathVerifier& verifier) : m_verifier(verifier), m_detected(false) {} + + bool isDetected() const { return m_detected; } + + st::GraphWalker::TVisitResult checkNode(st::ControlNode* node) { + if (st::InstructionNode* const candidate = node->cast()) { + if (candidate->getInstruction().mayCauseGC()) { + // Walking through the nodes seeking for consumer + // If it is found then candidate node may affect + // the consumer. Procucer should be protected. + m_verifier.run(candidate); + + if (m_verifier.isVerified()) { + // We found a node that may cause GC and it is really + // on the path between procducer and one of the consumers. + // We've just proved that the value should be protected. + + m_detected = true; + return st::GraphWalker::vrStopWalk; + + } else { + // Node may cause GC but control flow will never reach any of tracked consumers. + // This means that we may safely ignore this node and all it's subpaths. + + return st::GraphWalker::vrSkipPath; + } + } + } else if (st::PhiNode* const phi = node->cast()) { + // Phi node may not cause gc, protects it's value separately + // and do not have outgoing edges that we may traverse. + + return st::GraphWalker::vrSkipPath; + } else { + assert(false); + } + + return st::GraphWalker::vrKeepWalking; + } + +private: + st::PathVerifier& m_verifier; + bool m_detected; +}; + +class Walker : public st::GraphWalker { +public: + Walker(Detector& detector) : m_detector(detector) {} + +private: + Detector& m_detector; + virtual TVisitResult visitNode(st::ControlNode* node) { return m_detector.checkNode(node); } +}; + +bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) +{ + // We should protect the value by holder if consumer of this value is far away. + // By far away we mean that it crosses the barrier of potential garbage collection. + // For example if value is consumed right next to the point it was produced, then + // protection is not neccessary. Otherwise if there are memory allocation points in + // between then value protection is a must. + // + // In order to find out whether value protection is required, we trace the possible + // control- and data-flow paths and check what instructions are on the way between + // value producer and all potential consumers. If there is at least one dangerous + // operation on any of possible execution paths then the whole value should be protected. + + const st::TNodeSet& consumers = producer->getConsumers(); + + // In case of local domain reference we may apply fast check + if (consumers.size() == 1) { + st::ControlNode* const consumer = *consumers.begin(); + + if (producer->getDomain() == consumer->getDomain()) { + // Walking through the domain searching for dangerous nodes + + st::ControlNode* node = producer; + while (true) { + if (node == consumer) + return false; + + if (st::InstructionNode* const candidate = node->cast()) { + if (candidate->getInstruction().mayCauseGC()) + return true; + } else { + // There should be instruction nodes only + assert(false); + } + + assert(node->getOutEdges().size() == 1); + node = *node->getOutEdges().begin(); + } + } + } + + // Ok, it seem that fast lookup had failed. It means that we're dealing with + // a complex case that affects several domains and probably phi nodes. + // We need to perform a generic lookup walking through the whole graph. + + st::PathVerifier verifier(consumers); + verifier.addStopNode(producer); + + Detector detector(verifier); + + Walker walker(detector); + walker.addStopNode(producer); + + // Running detector for all incoming paths to consumers originating from producer. + // Walker will accumulate all safe paths and will not traverse any safe node twice. + + for (st::TNodeSet::iterator iConsumer = consumers.begin(); iConsumer != consumers.end(); ++iConsumer) { + st::ControlNode* const consumer = *iConsumer; + walker.run(consumer, st::GraphWalker::wdBackward); + + if (detector.isDetected()) + return true; + } + + // Changing direction to forward and performing last check starting from procucer. + // We need to reset the stop list because now we'll use differennt edges. + walker.resetStopNodes(); + + // When performing a forward run we need to end at consumers + walker.addStopNodes(consumers); + walker.run(producer, st::GraphWalker::wdForward); + + return detector.isDetected(); +} + void MethodCompiler::writePreamble(TJITContext& jit, bool isBlock) { Value* context = 0; From 9ae79ae75267bba845fb8ad3a17cd3270d7583b0 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 28 Feb 2015 19:42:49 +0600 Subject: [PATCH 141/290] Applies protectProducerNode() to produced values Issue: #32 --- src/MethodCompiler.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 96c7412..b8527de 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -572,7 +572,7 @@ void MethodCompiler::doPushInstance(TJITContext& jit) ss << "field" << index << "."; field->setName(ss.str()); - Value* const holder = protectPointer(jit, field); + Value* const holder = protectProducerNode(jit, jit.currentNode, field); setNodeValue(jit, jit.currentNode, holder); } @@ -599,7 +599,7 @@ void MethodCompiler::doPushArgument(TJITContext& jit) ss << "arg" << index << "."; argument->setName(ss.str()); - Value* const holder = protectPointer(jit, argument); + Value* const holder = protectProducerNode(jit, jit.currentNode, argument); setNodeValue(jit, jit.currentNode, holder); } @@ -618,7 +618,7 @@ void MethodCompiler::doPushTemporary(TJITContext& jit) ss << "temp" << index << "."; temporary->setName(ss.str()); - Value* const holder = protectPointer(jit, temporary); + Value* const holder = protectProducerNode(jit, jit.currentNode, temporary); setNodeValue(jit, jit.currentNode, holder); } @@ -634,8 +634,8 @@ void MethodCompiler::doPushLiteral(TJITContext& jit) ss << "lit" << (uint32_t) index << "."; literal->setName(ss.str()); - Value* const holder = protectPointer(jit, literal); - setNodeValue(jit, jit.currentNode, holder); + //Value* const holder = protectPointer(jit, literal); + setNodeValue(jit, jit.currentNode, literal); } void MethodCompiler::doPushConstant(TJITContext& jit) @@ -744,7 +744,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) blockObject = jit.builder->CreateBitCast(blockObject, m_baseTypes.object->getPointerTo()); blockObject->setName("block."); - Value* blockHolder = protectPointer(jit, blockObject); + Value* blockHolder = protectProducerNode(jit, jit.currentNode, blockObject); setNodeValue(jit, jit.currentNode, blockHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, blockHolder)); } @@ -793,7 +793,7 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) } Value* const argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); - Value* const argsHolder = protectPointer(jit, argumentsArray); + Value* const argsHolder = protectProducerNode(jit, jit.currentNode, argumentsArray); argsHolder->setName("pArgs."); setNodeValue(jit, jit.currentNode, argsHolder); @@ -855,7 +855,7 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv // Endoing phi and it's holder jit.builder->SetInsertPoint(currentBlock, currentBlock->getFirstInsertionPt()); PHINode* const phiValue = jit.builder->CreatePHI(m_baseTypes.object->getPointerTo(), phiNode->getIncomingList().size(), "phi."); - Value* const phiHolder = protectPointer(jit, phiValue); + Value* const phiHolder = protectProducerNode(jit, phiNode, phiValue); phiNode->setPhiValue(phiValue); @@ -866,12 +866,12 @@ Value* MethodCompiler::getNodeValue(TJITContext& jit, st::ControlNode* node, llv jit.builder->SetInsertPoint(currentBlock, storedInsertPoint); // Encoding the value load to be used in requesting instruction - value = jit.builder->CreateLoad(phiHolder); + value = (phiHolder == phiValue) ? static_cast(phiValue) : jit.builder->CreateLoad(phiHolder); } else { Value* const phiHolder = getPhiValue(jit, phiNode); jit.builder->SetInsertPoint(insertBlock, insertBlock->getTerminator()); - value = jit.builder->CreateLoad(phiHolder); + value = isa(phiHolder) ? jit.builder->CreateLoad(phiHolder) : phiHolder; } } @@ -897,7 +897,7 @@ llvm::Value* MethodCompiler::getPhiValue(TJITContext& jit, st::PhiNode* phiNode) // Resetting the insertion point which may be changed in getNodeValue() before. jit.builder->SetInsertPoint(phiInsertBlock, phiInsertBlock->getFirstInsertionPt()); - return protectPointer(jit, phiValue); + return protectProducerNode(jit, phiNode, phiValue); } void MethodCompiler::encodePhiIncomings(TJITContext& jit, st::PhiNode* phiNode) @@ -1033,7 +1033,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) phi->addIncoming(intResultObject, integersBlock); phi->addIncoming(sendMessageResult, sendBinaryBlock); - Value* const resultHolder = protectPointer(jit, phi); + Value* const resultHolder = protectProducerNode(jit, jit.currentNode, phi); setNodeValue(jit, jit.currentNode, resultHolder); //jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } @@ -1078,7 +1078,7 @@ void MethodCompiler::doSendMessage(TJITContext& jit) result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); } - Value* const resultHolder = protectPointer(jit, result); + Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); setNodeValue(jit, jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } @@ -1126,7 +1126,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) // value in the ControlGraph this will be redundant. Value* const original = getNodeValue(jit, jit.currentNode->getArgument()); - Value* const copy = protectPointer(jit, original); + Value* const copy = protectProducerNode(jit, jit.currentNode, original); setNodeValue(jit, jit.currentNode, copy); break; @@ -1192,7 +1192,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) m_callSiteIndexToOffset[m_callSiteIndex++] = jit.currentNode->getIndex(); Value* const result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); - Value* const resultHolder = protectPointer(jit, result); + Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); setNodeValue(jit, jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } break; From 8de8804c5b065b45dc8e83d2319e9793ef8d75b2 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 1 Mar 2015 00:45:36 +0600 Subject: [PATCH 142/290] Fixes protection logic and removes redundant checks --- include/analysis.h | 2 ++ src/MethodCompiler.cpp | 58 ++++++++++++++++++++++++++--------- src/TSmalltalkInstruction.cpp | 3 +- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index ee9120d..afaa1d3 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -538,6 +538,7 @@ class GraphWalker { assert(startNode); m_direction = direction; + m_stopNodes.erase(startNode); walkIn(startNode); nodesVisited(); } @@ -593,6 +594,7 @@ class PathVerifier : public ForwardWalker { void run(ControlNode* startNode) { assert(startNode); m_verified = false; + ForwardWalker::run(startNode); } diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index b8527de..1193b27 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -142,7 +142,12 @@ class Detector { st::GraphWalker::TVisitResult checkNode(st::ControlNode* node) { if (st::InstructionNode* const candidate = node->cast()) { if (candidate->getInstruction().mayCauseGC()) { - // Walking through the nodes seeking for consumer + outs() << "Detector noticed node " << candidate->getIndex() << " that may cause GC\n"; + + m_detected = true; + return st::GraphWalker::vrStopWalk; + + /* / Walking through the nodes seeking for consumer // If it is found then candidate node may affect // the consumer. Procucer should be protected. m_verifier.run(candidate); @@ -160,7 +165,7 @@ class Detector { // This means that we may safely ignore this node and all it's subpaths. return st::GraphWalker::vrSkipPath; - } + } */ } } else if (st::PhiNode* const phi = node->cast()) { // Phi node may not cause gc, protects it's value separately @@ -210,14 +215,24 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) if (producer->getDomain() == consumer->getDomain()) { // Walking through the domain searching for dangerous nodes - st::ControlNode* node = producer; + assert(producer->getOutEdges().size() == 1); + st::ControlNode* node = *producer->getOutEdges().begin(); + while (true) { - if (node == consumer) + if (node == consumer) { +// outs() << "Producer " << producer->getIndex() << " is safe and do not need a protection (1)\n"; + return false; + } if (st::InstructionNode* const candidate = node->cast()) { - if (candidate->getInstruction().mayCauseGC()) + if (candidate->getInstruction().mayCauseGC()) { + outs() << "Producer " << producer->getIndex() + << " should be protected because node " + << candidate->getIndex() << " may cause GC\n"; + return true; + } } else { // There should be instruction nodes only assert(false); @@ -240,6 +255,7 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) Walker walker(detector); walker.addStopNode(producer); + walker.addStopNodes(consumers); // Running detector for all incoming paths to consumers originating from producer. // Walker will accumulate all safe paths and will not traverse any safe node twice. @@ -248,19 +264,26 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) st::ControlNode* const consumer = *iConsumer; walker.run(consumer, st::GraphWalker::wdBackward); - if (detector.isDetected()) + if (detector.isDetected()) { + outs() << "Producer " << producer->getIndex() + << " should be protected because detector says that it is required\n"; + return true; + } } +// outs() << "Producer " << producer->getIndex() << " is safe and do not need a protection (2)\n"; + return false; + // Changing direction to forward and performing last check starting from procucer. // We need to reset the stop list because now we'll use differennt edges. - walker.resetStopNodes(); - - // When performing a forward run we need to end at consumers - walker.addStopNodes(consumers); - walker.run(producer, st::GraphWalker::wdForward); +// walker.resetStopNodes(); +// +// // When performing a forward run we need to end at consumers +// walker.addStopNodes(consumers); +// walker.run(producer, st::GraphWalker::wdForward); - return detector.isDetected(); +// return detector.isDetected(); } void MethodCompiler::writePreamble(TJITContext& jit, bool isBlock) @@ -381,6 +404,8 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Creating the function named as "Class>>method" or using provided one jit.function = methodFunction ? methodFunction : createFunction(method); + outs() << "Compiling " << jit.function->getName() << "\n"; + // { // std::ostringstream ss; // ss << "dots/" << jit.function->getName().data() << ".dot"; @@ -435,6 +460,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF m_blockFunctions.clear(); // m_targetToBlockMap.clear(); + outs() << "Done compiling method " << jit.function->getName() << "\n"; return jit.function; } @@ -634,8 +660,8 @@ void MethodCompiler::doPushLiteral(TJITContext& jit) ss << "lit" << (uint32_t) index << "."; literal->setName(ss.str()); - //Value* const holder = protectPointer(jit, literal); - setNodeValue(jit, jit.currentNode, literal); + Value* const holder = protectPointer(jit, literal); + setNodeValue(jit, jit.currentNode, holder); } void MethodCompiler::doPushConstant(TJITContext& jit) @@ -686,6 +712,8 @@ void MethodCompiler::doPushBlock(TJITContext& jit) ss << jit.originMethod->klass->name->toString() + ">>" + jit.originMethod->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); + outs() << "Compiling block " << blockFunctionName << "\n"; + // { // std::ostringstream ss; // ss << "dots/" << blockFunctionName << ".dot"; @@ -734,6 +762,8 @@ void MethodCompiler::doPushBlock(TJITContext& jit) // outs() << *blockContext.function << "\n"; } + outs() << "Done compiling block " << blockFunctionName << "\n"; + // Create block object and fill it with context information Value* args[] = { jit.getCurrentContext(), // creatingContext diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 6a68f15..9251885 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -85,7 +85,7 @@ bool st::TSmalltalkInstruction::isTrivial() const { case opcode::pushTemporary: case opcode::pushLiteral: case opcode::pushConstant: - case opcode::pushBlock: + // case opcode::pushBlock: case opcode::markArguments: return true; @@ -112,6 +112,7 @@ bool st::TSmalltalkInstruction::mayCauseGC() const { case opcode::assignInstance: return false; + case opcode::pushBlock: case opcode::sendUnary: case opcode::sendBinary: case opcode::sendMessage: From 0393e81d7d24d1ccd8cb93c44c303af44b662568 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 2 Mar 2015 23:41:49 +0600 Subject: [PATCH 143/290] Removes pointer protection from SendBinary arguments Issue: #32 --- src/MethodCompiler.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 1193b27..e47e282 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -660,7 +660,7 @@ void MethodCompiler::doPushLiteral(TJITContext& jit) ss << "lit" << (uint32_t) index << "."; literal->setName(ss.str()); - Value* const holder = protectPointer(jit, literal); + Value* const holder = protectProducerNode(jit, jit.currentNode, literal); setNodeValue(jit, jit.currentNode, holder); } @@ -1015,18 +1015,11 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // We need to create an arguments array and fill it with argument objects // Then send the message just like ordinary one - // Creation of argument array may cause the GC which will break the arguments - // We need to temporarily store them in a safe place - Value* leftValueHolder = protectPointer(jit, leftValue); - Value* rightValueHolder = protectPointer(jit, rightValue); - // Now creating the argument array Value* argumentsObject = createArray(jit, 2); - Value* restoredLeftValue = jit.builder->CreateLoad(leftValueHolder); - Value* restoredRightValue = jit.builder->CreateLoad(rightValueHolder); - jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(0), restoredLeftValue); - jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(1), restoredRightValue); + jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(0), leftValue); + jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(1), rightValue); Value* argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); Value* sendMessageArgs[] = { From ebb6d87bc68cdb2075350ece6dfe5444055da46b Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 4 Mar 2015 21:10:01 +0600 Subject: [PATCH 144/290] Messages sent to literal receivers are now encoded directly Literal receivers are encoded at the time of method compilation, so thier value and their class will not change over time. Actual values are known at compile time and may be used to lookup the actual method that should be invoked. Issue: #32 --- include/jit.h | 7 +- src/JITRuntime.cpp | 2 +- src/MethodCompiler.cpp | 172 ++++++++++++++++++++++++++++++++++ src/TSmalltalkInstruction.cpp | 2 +- 4 files changed, 180 insertions(+), 3 deletions(-) diff --git a/include/jit.h b/include/jit.h index 2c5315a..b3b4bd4 100644 --- a/include/jit.h +++ b/include/jit.h @@ -163,6 +163,8 @@ struct TBaseFunctions { } }; +class JITRuntime; + class MethodCompiler { public: @@ -241,6 +243,7 @@ class MethodCompiler { private: + JITRuntime& m_runtime; llvm::Module* m_JITModule; // std::map m_targetToBlockMap; void scanForBranches(TJITContext& jit, st::ParsedBytecode* source, uint32_t byteCount = 0); @@ -282,6 +285,7 @@ class MethodCompiler { void doSendUnary(TJITContext& jit); void doSendBinary(TJITContext& jit); void doSendMessage(TJITContext& jit); + bool doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode); void doSpecial(TJITContext& jit); void doPrimitive(TJITContext& jit); @@ -330,11 +334,12 @@ class MethodCompiler { TStackObject allocateStackObject(llvm::IRBuilder<>& builder, uint32_t baseSize, uint32_t fieldsCount); MethodCompiler( + JITRuntime& runtime, llvm::Module* JITModule, TRuntimeAPI runtimeApi, TExceptionAPI exceptionApi ) - : m_JITModule(JITModule), + : m_runtime(runtime), m_JITModule(JITModule), m_runtimeAPI(runtimeApi), m_exceptionAPI(exceptionApi), m_callSiteIndex(1) { m_baseTypes.initializeFromModule(JITModule); diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index c58715d..b83b53c 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -180,7 +180,7 @@ void JITRuntime::initialize(SmalltalkVM* softVM) createExecuteProcessFunction(); // Initializing the method compiler - m_methodCompiler = new MethodCompiler(m_JITModule, m_runtimeAPI, m_exceptionAPI); + m_methodCompiler = new MethodCompiler(*this, m_JITModule, m_runtimeAPI, m_exceptionAPI); // Initializing caches std::memset(&m_blockFunctionLookupCache, 0, sizeof(m_blockFunctionLookupCache)); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index e47e282..b617578 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -1061,8 +1062,179 @@ void MethodCompiler::doSendBinary(TJITContext& jit) //jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } + +bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode) +{ + // Optimized version of doSendMessage which takes into account that + // pending message should be sent to the literal receiver + // (either constant or a member of method literals). Literal receivers + // are encoded at the time of method compilation, so thier value and + // their class will not change over time. Moreover, actual values + // are known at compile time and may be used to lookup the actual + // method that should be invoked. + + // Locating message selector + TSymbolArray& literals = *jit.originMethod->literals; + TSymbol* const messageSelector = literals[jit.currentNode->getInstruction().getArgument()]; + + TObject* literalReceiver = 0; + + // Determining receiver class + const st::TSmalltalkInstruction::TOpcode opcode = receiverNode->getInstruction().getOpcode(); + if (opcode == opcode::pushLiteral) { + literalReceiver = literals[receiverNode->getInstruction().getArgument()]; + } else if (opcode == opcode::pushConstant) { + const uint8_t constant = receiverNode->getInstruction().getArgument(); + switch(constant) { + case 0: case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: case 9: + literalReceiver = TInteger(constant); + break; + + case pushConstants::nil: literalReceiver = globals.nilObject; break; + case pushConstants::trueObject: literalReceiver = globals.trueObject; break; + case pushConstants::falseObject: literalReceiver = globals.falseObject; break; + } + } + + assert(literalReceiver); + TClass* const receiverClass = isSmallInteger(literalReceiver) ? globals.smallIntClass : literalReceiver->getClass(); + + // Locating a method suitable for a direct call + TMethod* const directMethod = m_runtime.getVM()->lookupMethod(messageSelector, receiverClass); + + if (! directMethod) { + outs() << "Error! Could not lookup method for class " << receiverClass->name->toString() << ", selector " << messageSelector->toString() << "\n"; + return false; + } + + std::string directFunctionName = directMethod->klass->name->toString() + ">>" + messageSelector->toString(); + Function* directFunction = m_JITModule->getFunction(directFunctionName); + + if (!directFunction) { + // Compiling function and storing it to the table for further use + directFunction = compileMethod(directMethod); + + verifyFunction(*directFunction , llvm::AbortProcessAction); + + m_runtime.optimizeFunction(directFunction); + } + + // Allocating context object and temporaries on the methodFunction's stack. + // This operation does not affect garbage collector, so no pointer protection + // is required. Moreover, this is operation is much faster than heap allocation. + const bool hasTemporaries = directMethod->temporarySize > 0; + const uint32_t contextSize = sizeof(TContext); + const uint32_t tempsSize = hasTemporaries ? sizeof(TObjectArray) + sizeof(TObject*) * directMethod->temporarySize : 0; + + // Allocating stack space for objects and registering GC protection holder + + MethodCompiler::TStackObject contextPair = allocateStackObject(*jit.builder, sizeof(TContext), 0); + Value* contextSlot = contextPair.objectSlot; + Value* tempsSlot = 0; + + if (hasTemporaries) { + MethodCompiler::TStackObject tempsPair = allocateStackObject(*jit.builder, sizeof(TObjectArray), directMethod->temporarySize); + tempsSlot = tempsPair.objectSlot; + } + + // Filling stack space with zeroes + jit.builder->CreateMemSet( + contextSlot, // destination address + jit.builder->getInt8(0), // fill with zeroes + contextSize, // size of object slot + 0, // no alignment + false // volatile operation + ); + + if (hasTemporaries) + jit.builder->CreateMemSet( + tempsSlot, // destination address + jit.builder->getInt8(0), // fill with zeroes + tempsSize, // size of object slot + 0, // no alignment + false // volatile operation + ); + + // Initializing object fields + // TODO Move the init sequence out of the block or check that it is correctly optimized in loops + Value* newContextObject = jit.builder->CreateBitCast(contextSlot, m_baseTypes.object->getPointerTo(), "newContext."); + Value* newTempsObject = hasTemporaries ? jit.builder->CreateBitCast(tempsSlot, m_baseTypes.object->getPointerTo(), "newTemps.") : 0; + Function* setObjectSize = getBaseFunctions().setObjectSize; + Function* setObjectClass = getBaseFunctions().setObjectClass; + + // Object size stored in the TSize field of any ordinary object contains + // number of pointers except for the first two fields + const uint32_t contextFieldsCount = contextSize / sizeof(TObject*) - 2; + + jit.builder->CreateCall2(setObjectSize, newContextObject, jit.builder->getInt32(contextFieldsCount)); + jit.builder->CreateCall2(setObjectClass, newContextObject, getJitGlobals().contextClass); + + if (hasTemporaries) { + const uint32_t tempsFieldsCount = tempsSize / sizeof(TObject*) - 2; + jit.builder->CreateCall2(setObjectSize, newTempsObject, jit.builder->getInt32(tempsFieldsCount)); + jit.builder->CreateCall2(setObjectClass, newTempsObject, getJitGlobals().arrayClass); + } + + Function* setObjectField = getBaseFunctions().setObjectField; + Value* methodRawPointer = jit.builder->getInt32(reinterpret_cast(directMethod)); + Value* directMethodObject = jit.builder->CreateIntToPtr(methodRawPointer, m_baseTypes.object->getPointerTo()); + + Value* previousContext = jit.getCurrentContext(); // jit.builder->CreateLoad(info.contextHolder); + Value* contextObject = jit.builder->CreateBitCast(previousContext, m_baseTypes.object->getPointerTo()); + + Value* const arguments = getArgument(jit); + Value* messageArgumentsObject = jit.builder->CreateBitCast(arguments, m_baseTypes.object->getPointerTo()); + + jit.builder->CreateCall3(setObjectField, newContextObject, jit.builder->getInt32(0), directMethodObject); + jit.builder->CreateCall3(setObjectField, newContextObject, jit.builder->getInt32(1), messageArgumentsObject); + if (hasTemporaries) + jit.builder->CreateCall3(setObjectField, newContextObject, jit.builder->getInt32(2), newTempsObject); + else + jit.builder->CreateCall3(setObjectField, newContextObject, jit.builder->getInt32(2), getJitGlobals().nilObject); + jit.builder->CreateCall3(setObjectField, newContextObject, jit.builder->getInt32(3), contextObject); + + Value* newContext = jit.builder->CreateBitCast(newContextObject, m_baseTypes.context->getPointerTo()); + Value* result = 0; + + if (jit.methodHasBlockReturn) { + // Creating basic block that will be branched to on normal invoke + BasicBlock* const nextBlock = BasicBlock::Create(m_JITModule->getContext(), "next.", jit.function); + jit.currentNode->getDomain()->getBasicBlock()->setEndValue(nextBlock); + + // Performing a function invoke + result = jit.builder->CreateInvoke(directFunction, nextBlock, jit.exceptionLandingPad, newContext); + + // Switching builder to a new block + jit.builder->SetInsertPoint(nextBlock); + } else { + // Just calling the function. No block switching is required + result = jit.builder->CreateCall(directFunction, newContext); + } + + Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); + setNodeValue(jit, jit.currentNode, resultHolder); + + return true; +} + void MethodCompiler::doSendMessage(TJITContext& jit) { + + st::InstructionNode* const markArgumentsNode = jit.currentNode->getArgument()->cast(); + assert(markArgumentsNode); + + st::InstructionNode* const receiverNode = markArgumentsNode->getArgument()->cast(); + assert(receiverNode); + + // In case of a literal receiver we may encode direct method call + if (receiverNode->getInstruction().getOpcode() == opcode::pushLiteral || + receiverNode->getInstruction().getOpcode() == opcode::pushConstant) + { + if (doSendMessageToLiteral(jit, receiverNode)) + return; + } + Value* const arguments = getArgument(jit); // jit.popValue(); // First of all we need to get the actual message selector diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 9251885..f5360f9 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -113,7 +113,7 @@ bool st::TSmalltalkInstruction::mayCauseGC() const { return false; case opcode::pushBlock: - case opcode::sendUnary: +// case opcode::sendUnary: case opcode::sendBinary: case opcode::sendMessage: return true; From 557483f5bc7999e77a055e0fe6e6b6805890892b Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 4 Mar 2015 21:12:04 +0600 Subject: [PATCH 145/290] Encodes SmallInt literals as immediate values Issue: #32 --- src/MethodCompiler.cpp | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index b617578..c2e0fcf 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -653,16 +653,32 @@ void MethodCompiler::doPushLiteral(TJITContext& jit) { const uint8_t index = jit.currentNode->getInstruction().getArgument(); - Function* const getLiteralFromContext = m_JITModule->getFunction("getLiteralFromContext"); - Value* const context = jit.getCurrentContext(); - Value* const literal = jit.builder->CreateCall2(getLiteralFromContext, context, jit.builder->getInt32(index)); + TSymbolArray& literals = *jit.originMethod->literals; + TObject* const literal = literals[index]; - std::ostringstream ss; - ss << "lit" << (uint32_t) index << "."; - literal->setName(ss.str()); + Value* result = 0; - Value* const holder = protectProducerNode(jit, jit.currentNode, literal); - setNodeValue(jit, jit.currentNode, holder); + if (isSmallInteger(literal)) { + Value* const integerValue = jit.builder->getInt32( TInteger(literal).rawValue() ); + result = jit.builder->CreateIntToPtr(integerValue, m_baseTypes.object->getPointerTo()); + + std::ostringstream ss; + ss << "const" << result << "."; + result->setName(ss.str()); + } else { + Function* const getLiteralFromContext = m_JITModule->getFunction("getLiteralFromContext"); + Value* const context = jit.getCurrentContext(); + Value* const literalValue = jit.builder->CreateCall2(getLiteralFromContext, context, jit.builder->getInt32(index)); + + std::ostringstream ss; + ss << "lit" << (uint32_t) index << "."; + literalValue->setName(ss.str()); + + result = protectProducerNode(jit, jit.currentNode, literalValue); + } + + assert(result); + setNodeValue(jit, jit.currentNode, result); } void MethodCompiler::doPushConstant(TJITContext& jit) From 584a8a7446ed9875e3b15f486099b4cc080eb21a Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 4 Mar 2015 21:51:00 +0600 Subject: [PATCH 146/290] Adds trivial method detector Issue: #32 --- src/MethodCompiler.cpp | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index c2e0fcf..5cbd2f5 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -194,6 +194,35 @@ class Walker : public st::GraphWalker { virtual TVisitResult visitNode(st::ControlNode* node) { return m_detector.checkNode(node); } }; +bool MethodCompiler::methodAllocatesMemory(TJITContext& jit) +{ + class GCDetector : public st::ForwardWalker { + public: + GCDetector() : m_detected(false) {} + + virtual TVisitResult visitNode(st::ControlNode* node) { + if (st::InstructionNode* const candidate = node->cast()) { + if (candidate->getInstruction().mayCauseGC()) { + m_detected = true; + return st::GraphWalker::vrStopWalk; + } + } + + return st::GraphWalker::vrKeepWalking; + } + + bool isDetected() const { return m_detected; } + + private: + bool m_detected; + }; + + GCDetector detector; + detector.run((*jit.controlGraph->begin())->getEntryPoint()); + + return detector.isDetected(); +} + bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) { // We should protect the value by holder if consumer of this value is far away. @@ -305,7 +334,7 @@ void MethodCompiler::writePreamble(TJITContext& jit, bool isBlock) context->setName("contextParameter"); // Protecting the context holder - jit.contextHolder = protectPointer(jit, context); + jit.contextHolder = jit.methodIsTrivial ? context : protectPointer(jit, context); jit.contextHolder->setName("pContext"); // Storing self pointer @@ -313,18 +342,26 @@ void MethodCompiler::writePreamble(TJITContext& jit, bool isBlock) Value* arguments = jit.builder->CreateLoad(pargs); Value* pobject = jit.builder->CreateBitCast(arguments, m_baseTypes.object->getPointerTo()); Value* self = jit.builder->CreateCall2(m_baseFunctions.getObjectField, pobject, jit.builder->getInt32(0)); - jit.selfHolder = protectPointer(jit, self); + jit.selfHolder = jit.methodIsTrivial ? self : protectPointer(jit, self); jit.selfHolder->setName("pSelf"); } Value* MethodCompiler::TJITContext::getCurrentContext() { - return builder->CreateLoad(contextHolder, "context."); + // Trivial methods do not protect their context + if (isa(contextHolder)) + return builder->CreateLoad(contextHolder, "context."); + else + return contextHolder; } Value* MethodCompiler::TJITContext::getSelf() { - return builder->CreateLoad(selfHolder, "self."); + // Trivial methods do not protect their self pointer + if (isa(selfHolder)) + return builder->CreateLoad(selfHolder, "self."); + else + return selfHolder; } bool MethodCompiler::scanForBlockReturn(TJITContext& jit, uint32_t byteCount/* = 0*/) @@ -426,6 +463,8 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // all send message operations as invokes, not just simple calls jit.methodHasBlockReturn = scanForBlockReturn(jit); + jit.methodIsTrivial = !methodAllocatesMemory(jit); + // Writing the function preamble and initializing // commonly used pointers such as method arguments or temporaries writePreamble(jit); @@ -462,6 +501,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // m_targetToBlockMap.clear(); outs() << "Done compiling method " << jit.function->getName() << "\n"; + return jit.function; } From bf5d31b0d2fab5969a24dce0749706a872fd6d3a Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 4 Mar 2015 22:57:11 +0600 Subject: [PATCH 147/290] Refactors naming Issue: #32 --- include/jit.h | 6 ++++-- src/MethodCompiler.cpp | 12 +++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/jit.h b/include/jit.h index b3b4bd4..7a5c450 100644 --- a/include/jit.h +++ b/include/jit.h @@ -191,6 +191,7 @@ class MethodCompiler { llvm::BasicBlock* preamble; llvm::BasicBlock* exceptionLandingPad; bool methodHasBlockReturn; + bool methodAllocatesMemory; MethodCompiler* compiler; // link to outer class for variable access @@ -204,8 +205,8 @@ class MethodCompiler { TJITContext(MethodCompiler* compiler, TMethod* method, bool parse = true) : currentNode(0), originMethod(method), function(0), builder(0), - preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), compiler(compiler), - contextHolder(0), selfHolder(0) + preamble(0), exceptionLandingPad(0), methodHasBlockReturn(false), + methodAllocatesMemory(true), compiler(compiler), contextHolder(0), selfHolder(0) { if (parse) { parsedMethod = new st::ParsedMethod(method); @@ -267,6 +268,7 @@ class MethodCompiler { llvm::Value* protectPointer(TJITContext& jit, llvm::Value* value); llvm::Value* protectProducerNode(TJITContext& jit, st::ControlNode* node, llvm::Value* value); bool shouldProtectProducer(st::ControlNode* node); + bool methodAllocatesMemory(TJITContext& jit); void writePreamble(TJITContext& jit, bool isBlock = false); void writeFunctionBody(TJITContext& jit); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 5cbd2f5..7590545 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -334,7 +334,7 @@ void MethodCompiler::writePreamble(TJITContext& jit, bool isBlock) context->setName("contextParameter"); // Protecting the context holder - jit.contextHolder = jit.methodIsTrivial ? context : protectPointer(jit, context); + jit.contextHolder = jit.methodAllocatesMemory ? protectPointer(jit, context) : context; jit.contextHolder->setName("pContext"); // Storing self pointer @@ -342,14 +342,13 @@ void MethodCompiler::writePreamble(TJITContext& jit, bool isBlock) Value* arguments = jit.builder->CreateLoad(pargs); Value* pobject = jit.builder->CreateBitCast(arguments, m_baseTypes.object->getPointerTo()); Value* self = jit.builder->CreateCall2(m_baseFunctions.getObjectField, pobject, jit.builder->getInt32(0)); - jit.selfHolder = jit.methodIsTrivial ? self : protectPointer(jit, self); + jit.selfHolder = jit.methodAllocatesMemory ? protectPointer(jit, self) : self; jit.selfHolder->setName("pSelf"); } Value* MethodCompiler::TJITContext::getCurrentContext() { - // Trivial methods do not protect their context - if (isa(contextHolder)) + if (methodAllocatesMemory) return builder->CreateLoad(contextHolder, "context."); else return contextHolder; @@ -357,8 +356,7 @@ Value* MethodCompiler::TJITContext::getCurrentContext() Value* MethodCompiler::TJITContext::getSelf() { - // Trivial methods do not protect their self pointer - if (isa(selfHolder)) + if (methodAllocatesMemory) return builder->CreateLoad(selfHolder, "self."); else return selfHolder; @@ -463,7 +461,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // all send message operations as invokes, not just simple calls jit.methodHasBlockReturn = scanForBlockReturn(jit); - jit.methodIsTrivial = !methodAllocatesMemory(jit); + jit.methodAllocatesMemory = methodAllocatesMemory(jit); // Writing the function preamble and initializing // commonly used pointers such as method arguments or temporaries From 69e3a6165878255a71f0a7500bac392323a5b7ad Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Thu, 5 Mar 2015 00:30:42 +0600 Subject: [PATCH 148/290] Adds optimization for PushArgument 0 Self pointer is already stored in holder for quick access Issue: #32 --- src/MethodCompiler.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 7590545..f963f47 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -643,19 +643,14 @@ void MethodCompiler::doPushInstance(TJITContext& jit) void MethodCompiler::doPushArgument(TJITContext& jit) { - /* const st::TNodeList& consumers = jit.currentNode->getConsumers(); - if (consumers.empty()) { - assert(false); + const uint8_t index = jit.currentNode->getInstruction().getArgument(); + + if (index == 0) { + // Optimizing self + setNodeValue(jit, jit.currentNode, jit.selfHolder); return; } - // If we have only one consumer it's better to encode instruction near it. - // It would be consumer's responsibility to encode us before it's site. - if (consumers.size() == 1) - return; */ - - const uint8_t index = jit.currentNode->getInstruction().getArgument(); - Function* const getArgFromContext = m_JITModule->getFunction("getArgFromContext"); Value* const context = jit.getCurrentContext(); Value* const argument = jit.builder->CreateCall2(getArgFromContext, context, jit.builder->getInt32(index)); From aa4b4e9fe99bc8905e7bd9962845cb50d40d8323 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Mar 2015 22:42:08 +0300 Subject: [PATCH 149/290] Removes redundant gc protection in doMarkArguments Issue: #32 --- src/MethodCompiler.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index f963f47..f9d3212 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -871,13 +871,9 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) Value* const argument = getArgument(jit, index - 1); // jit.popValue(); jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), argument); } - Value* const argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); - Value* const argsHolder = protectProducerNode(jit, jit.currentNode, argumentsArray); - argsHolder->setName("pArgs."); - setNodeValue(jit, jit.currentNode, argsHolder); - // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); + setNodeValue(jit, jit.currentNode, argumentsArray); } void MethodCompiler::doSendUnary(TJITContext& jit) From 7cf2f5cd63480f1de3ee82f89e3476b39de3e36b Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Mon, 16 Feb 2015 14:26:25 +0300 Subject: [PATCH 150/290] Modifies FindLLVM.cmake to switch between versions in an easy way. Issue: #61 --- CMakeLists.txt | 9 ++-- cmake/FindLLVM.cmake | 120 ++++++++++++++++++++++++++++++++----------- 2 files changed, 94 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07ed8b2..5d244c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,13 +2,14 @@ cmake_minimum_required(VERSION 2.8.4) set (CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_SOURCE_DIR}/cmake/variables.cmake") set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +set (LLVM_PACKAGE_VERSION 3.3) project(llst-project) find_package(Threads REQUIRED QUIET) find_package(READLINE) find_package(TINFO) -find_package(LLVM 3.3 EXACT) +find_package(LLVM ${LLVM_PACKAGE_VERSION} EXACT) find_package(POD2MAN) find_package(GZIP REQUIRED) @@ -19,17 +20,15 @@ option(USE_POD2MAN "Should we use pod2man to build the documentation (we will cr if (USE_LLVM) if (LLVM_FOUND) message(STATUS "Using LLVM ${LLVM_VERSION}") - set (CMAKE_C_FLAGS "${LLVM_C_FLAGS} ${CMAKE_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${LLVM_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") set (CMAKE_EXE_LINKER_FLAGS "${LLVM_LD_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}") # LLVM generates loads of warnings... set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") set (LLVM_LIBS_TO_LINK ${LLVM_LIBS}) - add_definitions(-DLLVM) + add_definitions(-DLLVM ${LLVM_CPP_FLAGS}) else() - message(FATAL_ERROR "\nInstall llvm-3.3-dev:i386 and try again.") + message(FATAL_ERROR "\nInstall llvm-${LLVM_PACKAGE_VERSION}-dev:i386 and try again.") endif() else() message(STATUS "LLVM is disabled") diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake index d2a58cd..25d916a 100644 --- a/cmake/FindLLVM.cmake +++ b/cmake/FindLLVM.cmake @@ -4,20 +4,41 @@ # Export variables: # LLVM_FOUND # LLVM_CONFIG_EXE +# LLVM_CPP_FLAGS # LLVM_CXX_FLAGS # LLVM_C_FLAGS # LLVM_LD_FLAGS # LLVM_INSTALL_PREFIX # LLVM_VERSION # LLVM_LIBS +# LLVM_LIBFILES # LLVM_INCLUDE_DIR +# LLVM_LIB_DIR include(CheckIncludeFileCXX) include(CheckCXXSourceCompiles) include(CMakePushCheckState) include(FindPackageHandleStandardArgs) -macro(get_llvm_config_var args out_var) +if(LLVM_FOUND AND PREVIOUS_FOUND_VERSION EQUAL LLVM_FIND_VERSION) + # No need to find it again. + return() +else() + unset(LLVM_FOUND CACHE) + unset(LLVM_PREVIOUS_FOUND_VERSION CACHE) + unset(LLVM_CONFIG_EXE CACHE) + unset(LLVM_LIBS_INSTALLED CACHE) + unset(LLVM_HEADERS_INSTALLED CACHE) + unset(LLVM_COMPILED_AND_LINKED CACHE) +endif() + +message(STATUS "Looking for LLVM ${LLVM_FIND_VERSION}") + +function(get_llvm_config_var args out_var) + unset(${out_var} CACHE) + if (NOT LLVM_CONFIG_EXE) + return() + endif() execute_process( COMMAND ${LLVM_CONFIG_EXE} ${args} OUTPUT_VARIABLE ${out_var} @@ -28,53 +49,92 @@ macro(get_llvm_config_var args out_var) if (exit_code) message(SEND_ERROR "Executing '${LLVM_CONFIG_EXE} ${args}' exited with '${exit_code}'") message(FATAL_ERROR "Error message: ${std_err_output}") + else() + set(${out_var} "${${out_var}}" CACHE INTERNAL "${out_var}") + endif() +endfunction() + +macro(check_llvm_libs out_var) + if (${out_var}) + return() # Result is cached endif() + + set (libs_exist YES) + + string(REPLACE " " ";" LIBS_LIST "${LLVM_LIBFILES}") + if (NOT LIBS_LIST) + set (libs_exist NOTFOUND) + endif() + + foreach(lib ${LIBS_LIST}) + if (NOT EXISTS ${lib}) + message(STATUS "File ${lib} is missing") + set (libs_exist NOTFOUND) + break() + endif() + endforeach() + + set (${out_var} "${libs_exist}" CACHE INTERNAL "") endmacro() macro(check_llvm_header header out_var) - cmake_push_check_state() + CMAKE_PUSH_CHECK_STATE() set(CMAKE_REQUIRED_FLAGS "${LLVM_CXX_FLAGS}") CHECK_INCLUDE_FILE_CXX("${header}" ${out_var}) - cmake_pop_check_state() + CMAKE_POP_CHECK_STATE() endmacro() macro(check_llvm_source_compiles code out_var) - cmake_push_check_state() - set(CMAKE_REQUIRED_FLAGS "${LLVM_CXX_FLAGS} ${LLVM_LD_FLAGS}") - set(CMAKE_REQUIRED_LIBRARIES ${LLVM_LIBS} dl pthread) + CMAKE_PUSH_CHECK_STATE() + set(CMAKE_REQUIRED_FLAGS ${LLVM_CXX_FLAGS}) + set(CMAKE_REQUIRED_LIBRARIES ${LLVM_LIBS} ${LLVM_LD_FLAGS}) CHECK_CXX_SOURCE_COMPILES("${code}" ${out_var}) - cmake_pop_check_state() + CMAKE_POP_CHECK_STATE() endmacro() set(LLVM_CONFIG_NAMES "llvm-config-${LLVM_FIND_VERSION}" llvm-config) -foreach(version ${LLVM_ADDITIONAL_VERSIONS} 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.5) - list(APPEND LLVM_CONFIG_NAMES "llvm-config-${version}") -endforeach() +if (NOT LLVM_FIND_VERSION_EXACT) + foreach(version 3.1 3.2 3.3 3.4 3.5) + list(APPEND LLVM_CONFIG_NAMES "llvm-config-${version}") + endforeach() +endif() find_program(LLVM_CONFIG_EXE NAMES ${LLVM_CONFIG_NAMES} DOC "Full path to llvm-config") -unset(LLVM_CONFIG_NAMES) - -if (LLVM_CONFIG_EXE) - get_llvm_config_var(--cxxflags LLVM_CXX_FLAGS) - get_llvm_config_var(--cflags LLVM_C_FLAGS) - get_llvm_config_var(--ldflags LLVM_LD_FLAGS) - get_llvm_config_var(--prefix LLVM_INSTALL_PREFIX) - get_llvm_config_var(--version LLVM_VERSION) - get_llvm_config_var(--libs LLVM_LIBS) - get_llvm_config_var(--includedir LLVM_INCLUDE_DIR) - - # Header 'Pass.h' locates in 'include/llvm/' directory since 1.9 till 3.4 release - check_llvm_header("llvm/Pass.h" LLVM_PASS_H) - check_llvm_source_compiles("#include \n int main(){ return 0; }" LLVM_PASSES_LINKED) - - if (NOT LLVM_PASS_H OR NOT LLVM_PASSES_LINKED) - message(STATUS "See ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log for details") - endif() +mark_as_advanced(LLVM_CONFIG_EXE) +if (NOT LLVM_CONFIG_EXE) + message(STATUS "Could NOT find llvm-config (tried ${LLVM_CONFIG_NAMES})") +endif() + +get_llvm_config_var(--cppflags LLVM_CPP_FLAGS) +get_llvm_config_var(--cxxflags LLVM_CXX_FLAGS) +get_llvm_config_var(--cflags LLVM_C_FLAGS) +get_llvm_config_var(--ldflags LLVM_LD_FLAGS) +get_llvm_config_var(--prefix LLVM_INSTALL_PREFIX) +get_llvm_config_var(--version LLVM_VERSION) +get_llvm_config_var(--libs LLVM_LIBS) +get_llvm_config_var(--libfiles LLVM_LIBFILES) +get_llvm_config_var(--includedir LLVM_INCLUDE_DIR) +get_llvm_config_var(--libdir LLVM_LIB_DIR) + +check_llvm_libs(LLVM_LIBS_INSTALLED) +check_llvm_header("llvm/Support/TargetSelect.h" LLVM_HEADERS_INSTALLED) +check_llvm_source_compiles("#include \n int main(){ return 0; }" LLVM_COMPILED_AND_LINKED) + +if (LLVM_HEADERS_INSTALLED AND NOT LLVM_LIBS_INSTALLED) + message(STATUS "Only header files installed in the package") +elseif (LLVM_LIBS_INSTALLED AND NOT LLVM_HEADERS_INSTALLED) + message(STATUS "Libs installed while header files are missing") +elseif (LLVM_HEADERS_INSTALLED AND LLVM_LIBS_INSTALLED AND NOT LLVM_COMPILED_AND_LINKED) + message(STATUS "Libs and headers are installed, but during test compilation something went wrong. See ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log for details") endif() FIND_PACKAGE_HANDLE_STANDARD_ARGS( LLVM - REQUIRED_VARS LLVM_CONFIG_EXE LLVM_PASS_H LLVM_PASSES_LINKED + FOUND_VAR LLVM_FOUND + REQUIRED_VARS LLVM_CONFIG_EXE LLVM_LIBS_INSTALLED LLVM_HEADERS_INSTALLED LLVM_COMPILED_AND_LINKED VERSION_VAR LLVM_VERSION ) -mark_as_advanced(LLVM_CONFIG_EXE) +set(LLVM_FOUND ${LLVM_FOUND} CACHE INTERNAL "LLVM_FOUND") +if (LLVM_FOUND) + set(PREVIOUS_FOUND_VERSION ${LLVM_VERSION} CACHE INTERNAL "The version of LLVM found by previous call find_package") +endif() From d1ba916a467a08195291f4ad49217e51a1f995ac Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Mar 2015 02:11:44 +0300 Subject: [PATCH 151/290] Prettifies LLVM flags Issue: #61 --- CMakeLists.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d244c8..5cb76f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,13 +20,11 @@ option(USE_POD2MAN "Should we use pod2man to build the documentation (we will cr if (USE_LLVM) if (LLVM_FOUND) message(STATUS "Using LLVM ${LLVM_VERSION}") - set (CMAKE_EXE_LINKER_FLAGS "${LLVM_LD_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}") - # LLVM generates loads of warnings... - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") - + set (CMAKE_CXX_FLAGS "${LLVM_CPP_FLAGS} ${CMAKE_CXX_FLAGS} -Wno-unused-parameter") set (LLVM_LIBS_TO_LINK ${LLVM_LIBS}) - add_definitions(-DLLVM ${LLVM_CPP_FLAGS}) + link_directories(${LLVM_LIB_DIR}) + add_definitions(-DLLVM) else() message(FATAL_ERROR "\nInstall llvm-${LLVM_PACKAGE_VERSION}-dev:i386 and try again.") endif() From 431c1e9db4ef26608af86e4cba7eb5db138ad68d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Mar 2015 02:32:36 +0300 Subject: [PATCH 152/290] Hides variables from user corresponding to readline and tinfo Issue: #61 --- cmake/FindREADLINE.cmake | 10 +++------- cmake/FindTINFO.cmake | 5 ++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/cmake/FindREADLINE.cmake b/cmake/FindREADLINE.cmake index 16c3380..913f8f3 100644 --- a/cmake/FindREADLINE.cmake +++ b/cmake/FindREADLINE.cmake @@ -5,13 +5,9 @@ # READLINE_INCLUDE_DIRS # READLINE_LIBRARIES -find_path(READLINE_INCLUDE_DIRS - NAMES readline/readline.h -) - -find_library(READLINE_LIBRARIES - NAMES readline -) +find_path(READLINE_INCLUDE_DIRS NAMES readline/readline.h) +find_library(READLINE_LIBRARIES NAMES readline) +mark_as_advanced(READLINE_INCLUDE_DIRS READLINE_LIBRARIES) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(readline DEFAULT_MSG READLINE_LIBRARIES READLINE_INCLUDE_DIRS ) diff --git a/cmake/FindTINFO.cmake b/cmake/FindTINFO.cmake index 8459ec8..5491120 100644 --- a/cmake/FindTINFO.cmake +++ b/cmake/FindTINFO.cmake @@ -5,9 +5,8 @@ # TINFO_INCLUDE_DIRS # TINFO_LIBRARIES -find_library(TINFO_LIBRARIES - NAMES tinfo -) +find_library(TINFO_LIBRARIES NAMES tinfo) +mark_as_advanced(TINFO_LIBRARIES) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS( tinfo DEFAULT_MSG TINFO_LIBRARIES ) From fdbffa050968d6101f8aec40460e93bc0c9afb5f Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Mar 2015 03:18:55 +0300 Subject: [PATCH 153/290] Adds deps for DEB package + changes the name of package Issue: #61 --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cb76f0..9757e74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set (CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_SOURCE_DIR}/cmake/variables.cmake") set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set (LLVM_PACKAGE_VERSION 3.3) -project(llst-project) +project(llst) find_package(Threads REQUIRED QUIET) find_package(READLINE) @@ -132,8 +132,10 @@ set (CPACK_STRIP_FILES "llst") set (CPACK_GENERATOR "DEB") +set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://llst.org") set (CPACK_DEBIAN_PACKAGE_MAINTAINER "Team ") -set (CPACK_DEBIAN_PACKAGE_DEPENDS "libc6") +set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "i386") +set (CPACK_DEBIAN_PACKAGE_DEPENDS "libc6:i386, libgcc1:i386, libstdc++6:i386, libreadline6:i386, libtinfo5:i386") include(CPack) From 23c05a515d85fb976289ab9b916befaef3457789 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Mar 2015 05:13:10 +0300 Subject: [PATCH 154/290] Fixes the version of doc Issue: #61 --- doc/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index e61b20d..151562a 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -5,7 +5,7 @@ set (MAN_LLST_EN_GZ ${CMAKE_CURRENT_BINARY_DIR}/llst.1.en.gz) get_pod2man_cmd(MAN_LLST_EN_CMD FROM ${POD_LLST_EN} TO ${MAN_LLST_EN} SECTION 1 CENTER "LLST Documentation" - RELEASE 0.2.1 + RELEASE 0.3.0 NAME llst ) add_custom_command( From 6e84ebcee2bdf4f1bf7338a99d684e6ab7468f76 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Mar 2015 05:18:40 +0300 Subject: [PATCH 155/290] Improves the man page Issue: #62 --- doc/llst.1.en.pod | 63 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/doc/llst.1.en.pod b/doc/llst.1.en.pod index 5c1910a..84a46f9 100644 --- a/doc/llst.1.en.pod +++ b/doc/llst.1.en.pod @@ -1,15 +1,72 @@ #pod2man --section=1 --center="LLST Documentation" --release=0.2 --name=llst ./doc/llst.1.en.pod | gzip -9 > ./doc/llst.1.en.gz +#POD examples: L + +=encoding utf8 + =head1 NAME -llst - a SmallTalk virtual machine +llst - Low Level SmallTalk + +=head1 SYNOPSIS + +B [B<-h> heap_size] [B<-H> max_heap_size] [[B<-i>] image] =head1 DESCRIPTION -TODO: +B - a SmallTalk virtual machine with JIT based on LLVM. + +=head2 OPTIONS + +=over 6 + +=item B<-i> image, B<--image=>image + + Path to image + +=item B<-h> heap_size, B<--heap=>heap_size + + Init size of the VM heap + +=item B<-H> max_heap_size, B<--heap_max=>max_heap_size + + Max size of the VM heap + +=item B<--help> + Display short help and quit + +=item B<-V>, B<--version> + + Display version and quit + +=back + +=head1 BUGS + +Email bug reports to bugs@llst.org. + +=head1 COPYRIGHT + + Copyright 2012-2015 by Dmitry Kashitsyn + Copyright 2012-2015 by Roman Proskuryakov + + Copyright 1987-2005 by Timothy A. Budd + Copyright 2007 by Charles R. Childers + Copyright 2005-2007 by Danny Reinhold + +Original license of LittleSmalltalk may be found in the copyright file. + +You should have received a copy of the GNU General Public License +along with LLST. If not, see L. =head1 SEE ALSO -POD examples: L +=over 4 + +=item * "Getting Started with LLVM Core Libraries", Bruno Cardoso Lopes, Rafael Auler, Packt Publishing, 2014, ISBN 978-1-78216-692-4. + +=item * "A Little Smalltalk", Timothy Budd, Addison-Wesley, 1987, ISBN 0-201-10698-1. + +=back =cut From d0f2d06cf153baa083163bd81b3047a0ce89c5ee Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 7 Mar 2015 21:36:56 +0600 Subject: [PATCH 156/290] Experimental implementation of self direct calls and lifetime markers Commit provides experimental implementation of direct calls of messages sent to self where it is safe to do so. If class do not have children message sent to self may be encoded as a direct call. However, some image code is required to detect and store children of current class. Also argument arrays produced by MarkArguments are now enclosed in llvm.lifetime.start/end intrinsics to mark the object lifetime. Issue: #32 --- image/imageSource.st | 224 +++++++++++++++++++++++++---------------- include/jit.h | 10 +- src/JITRuntime.cpp | 52 +++++++--- src/MethodCompiler.cpp | 111 ++++++++++++++------ 4 files changed, 259 insertions(+), 138 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index 8dab527..8aeb582 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -49,7 +49,7 @@ COMMENT RAWCLASS doesn't automatically build Meta class; CLASS does COMMENT----------------------------------------------------------- COMMENT The strange circular world at the beginning COMMENT RAWCLASS Object MetaObject nil -RAWCLASS Class MetaClass Object name parentClass methods size variables package +RAWCLASS Class MetaClass Object name parentClass methods size variables children RAWCLASS MetaObject Class Class RAWCLASS MetaClass Class MetaObject COMMENT-------------------------------------------------------- @@ -276,7 +276,7 @@ name: n parent: c variables: v methods <- Dictionary new. size <- v size + c size. variables <- v. - package <- nil. + children <- nil. ! METHOD Class size @@ -302,6 +302,29 @@ METHOD Class subclasses self subclasses: 0 ! + +METHOD Class +children + ^children +! + +METHOD Class +registerChild: aClass + children isNil ifTrue: [ children <- List new ]. + children addLast: aClass. +! + +METHOD MetaClass +fillChildren |parent| + globals do: [ :obj | + (obj isKindOf: Class) + ifTrue: [ + parent <- obj parent. + parent notNil ifTrue: [ parent registerChild: obj ] + ] + ] +! + METHOD Class subclasses: indent globals do: [ :obj | @@ -382,7 +405,6 @@ addMethod: text | meth | meth notNil ifTrue: [ methods at: meth name put: meth. Method flushCache. - 'ok' printNl. ^ meth ]. ^ nil @@ -1531,6 +1553,11 @@ printMethod: aMethod <249 aMethod> ! +METHOD MetaJit +printMethod: aSelector ofClass: aClass + self printMethod: (aClass methods at: aSelector) +! + METHOD MetaJit patchHotMethods <250> @@ -1631,7 +1658,7 @@ pushConstants |testBlock| METHOD Undefined sortTest | list | list <- List new. - 1 to: 100 do: [ :x | list add: 1000 atRandom ]. + 1 to: 10000 do: [ :x | list add: 1000 atRandom ]. list <- list sort. ^list @@ -1694,32 +1721,24 @@ runSortBenchmarkFor: data ! METHOD MetaSystem -rebuildImage | classes | +rebuildImage | classes methods | classes <- List new. globals do: [ :global | (global isKindOf: Class) ifTrue: [ classes add: global ] ]. + methods <- List new. classes do: [ :class | class methods do: - [ :method | self rebuildMethod: method for: class ] ] + [ :method | methods add: + (self rebuildMethod: method for: class) ] ]. ! METHOD MetaSystem -rebuildMethod: aMethod for: aClass | newMethod | - (aClass = Undefined) - ifTrue: [ - 'Compiling ' print. aClass print. ':' print. - aMethod name print. '... ' print. - ]. - - newMethod <- aClass parseMethod: aMethod text. - newMethod notNil - ifTrue: [ 'ok' printNl ] - ifFalse: [ ^nil ]. +rebuildMethod: aMethod for: aClass + "'Compiling ' print. aClass print. ':' print. + aMethod name print. '... ' printNl." - self assignClass: aClass to: newMethod. - aClass methods at: (aMethod name) put: newMethod. - Method flushCache. + ^aClass parseMethod: aMethod text. ! METHOD MetaSystem @@ -1759,36 +1778,59 @@ fixMethodClasses | classes | [ :method | self assignClass: class to: method ] ] ! +METHOD Undefined +inlineTest1 + ^42 +! + +METHOD Undefined +inlineTest2 + ^'Hello world' +! + +METHOD Undefined +inlineTest3 + ^self inlineTest1 +! + +METHOD Undefined +inlineTest4 + ^'Hello world' printString +! + +METHOD Undefined +inlineTest |x y z t| + x <- self inlineTest1. + y <- self inlineTest2. + z <- self inlineTest3. + t <- self inlineTest4. +! + +METHOD Undefined +factTest: x + ^(x = 1) ifTrue: [1] ifFalse: [self factTest: x - 1] +! + METHOD Undefined main | command data x | Char initialize. - "System fixMethodClasses." - - "SmallIntTest new runAll. - ArrayTest new runAll. - PrimitiveTest new runAll. - MagnitudeTest new runAll. - ListTest new runAll. - MethodLookupTest new runAll." + Class fillChildren. + System fixMethodClasses. + "Jit do: [ nil inlineTest ]." "Scheduler initialize. Thread run: [ 1 to: 10 do: [ :x | 'A' printNl ] ]. Thread run: [ 1 to: 10 do: [ :x | 'B' printNl ] ]. Scheduler run." - "'Preparing test data ...' print. - data <- List new. - 1 to: 1000 do: [ :x | data add: 100000 atRandom ]. - 'done' printNl. + "'Soft run: ' print. (Timer millisecondsToRun: [ System rebuildImage ]) printNl." + "'Cold jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl." + "'Hot jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl." - 'Soft run: ' print. (Timer millisecondsToRun: [ data sort ]) printNl. - 'Cold jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ data sort ] ]) printNl. - 'Hot jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ data sort ] ]) printNl. + "Jit patchHotMethods. - Jit patchHotMethods. - - 'Patched cold jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ data sort ] ]) printNl. - 'Patched hot jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ data sort ] ]) printNl." + 'Patched cold jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl. + 'Patched hot jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl." "1 to: 100 do: [ :x | Jit do: [ nil loopBenchmark ] ]." @@ -1796,7 +1838,9 @@ main | command data x | "Jit do: [ nil runSort: 10000 ]." "Jit do: [ 'hello' printNl ]." - "Jit do: [ System rebuildImage ]." + "Jit do: [ System rebuildImage ]. + Jit printStatistics. + Jit patchHotMethods." "Jit do: [ [ :p | x <- p ] value: 42 ]." "Jit do: [ SmallIntTest new runAll ]." @@ -2488,9 +2532,8 @@ insertSort: criteria | result | METHOD Collection sort: criteria | left right mediane | - "(self size < 2) ifTrue: [^self]." - (self size < 32) - ifTrue: [ ^ self insertSort: criteria ]. + (self size < 2) ifTrue: [^self]. + "(self size < 32) ifTrue: [ ^ self insertSort: criteria ]." mediane <- self popFirst. "if self is instance of List then call List>>popFirst" @@ -4433,10 +4476,6 @@ parse: c with: encoderClass | encoder meth name | self nextLex. encoder <- encoderClass new. name <- self readMethodName. - (c ~= Undefined) ifTrue: [ - 'Compiling ' print. c print. ':' print. - name print. '... ' print - ]. encoder name: name. self readMethodVariables. self readBody compile: encoder block: false. @@ -4461,13 +4500,16 @@ currentChar ^ text at: index ifAbsent: [ Char eof ] ! METHOD Parser -nextChar - | c | - c <- self currentChar. - (c = Char newline) ifTrue: [ lineNum <- lineNum + 1 ]. - index <- index + 1. - (c = Char cr) ifTrue: [ ^self nextChar ] - ^self currentChar +nextChar | c | + c <- self currentChar. + (c = Char newline) + ifTrue: [ lineNum <- lineNum + 1 ]. + + index <- index + 1. + (c = Char cr) + ifTrue: [ ^self nextChar ]. + + ^self currentChar ! METHOD Parser nextLex @@ -4670,7 +4712,7 @@ nameNode: name (name == (instNames at: i)) ifTrue: [ ^ (InstNode at: lineNum) position: i ] ]. - ^ (LiteralNode at: lineNum); + ^ (LiteralNode at: lineNum) value: (globals at: name ifAbsent: [ ^ self error: 'unrecognized name: ' + name printString ]) @@ -4778,43 +4820,53 @@ readBlockTemporaries ! METHOD Parser readCascade: base | node list | - node <- self keywordContinuation: base. - tokenType = $; - ifTrue: [ node <- (CascadeNode at: lineNum) head: node. - list <- List new. - [ tokenType = $; ] - whileTrue: [ self nextLex. - list add: - (self keywordContinuation: nil ) ]. - node list: list ]. - ^ node + node <- self keywordContinuation: base. + + tokenType = $; ifTrue: [ + node <- (CascadeNode at: lineNum) head: node. + list <- List new. + + [ tokenType = $; ] whileTrue: [ + self nextLex. + list add: (self keywordContinuation: nil) + ]. + + node list: list + ]. + + ^ node ! METHOD Parser keywordContinuation: base | receiver name args lnum | - receiver <- self binaryContinuation: base. - self tokenIsKeyword - ifFalse: [ ^ receiver ]. - name <- ''. - args <- List new. - lnum <- lineNum. - [ self tokenIsKeyword ] - whileTrue: [ name <- name + token. self nextLex. - args add: - (self binaryContinuation: self readTerm) ]. - ^ (MessageNode at: lnum) receiver: - receiver name: name asSymbol arguments: args + receiver <- self binaryContinuation: base. + self tokenIsKeyword ifFalse: [ ^receiver ]. + + name <- ''. + args <- List new. + lnum <- lineNum. + + [ self tokenIsKeyword ] whileTrue: [ + name <- name + token. self nextLex. + args add: (self binaryContinuation: self readTerm) ]. + + ^ (MessageNode at: lnum) + receiver: receiver + name: name asSymbol + arguments: args ! METHOD Parser binaryContinuation: base | receiver name lnum | - receiver <- self unaryContinuation: base. - [ self tokenIsBinary] - whileTrue: [ lnum <- lineNum. - name <- token asSymbol. self nextLex. - receiver <- (MessageNode at: lnum) - receiver: receiver name: name arguments: - (List with: - (self unaryContinuation: self readTerm)) ]. - ^ receiver + receiver <- self unaryContinuation: base. + + [ self tokenIsBinary ] whileTrue: [ + lnum <- lineNum. + name <- token asSymbol. self nextLex. + receiver <- (MessageNode at: lnum) + receiver: receiver + name: name + arguments: (List with: (self unaryContinuation: self readTerm)) ]. + + ^receiver ! METHOD Parser unaryContinuation: base | receiver lnum | diff --git a/include/jit.h b/include/jit.h index 7a5c450..2d952ca 100644 --- a/include/jit.h +++ b/include/jit.h @@ -165,6 +165,8 @@ struct TBaseFunctions { class JITRuntime; +typedef std::pair TObjectAndSize; + class MethodCompiler { public: @@ -287,7 +289,7 @@ class MethodCompiler { void doSendUnary(TJITContext& jit); void doSendBinary(TJITContext& jit); void doSendMessage(TJITContext& jit); - bool doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode); + bool doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode, TClass* receiverClass = 0); void doSpecial(TJITContext& jit); void doPrimitive(TJITContext& jit); @@ -304,7 +306,7 @@ class MethodCompiler { llvm::Value*& primitiveResult, llvm::BasicBlock* primitiveFailedBB); - llvm::Value* createArray(TJITContext& jit, uint32_t elementsCount); + TObjectAndSize createArray(TJITContext& jit, uint32_t elementsCount); llvm::Function* createFunction(TMethod* method); uint16_t getSkipOffset(st::InstructionNode* branch); @@ -466,6 +468,8 @@ class JITRuntime { llvm::Value* contextHolder; llvm::Value* tempsHolder; + + TDirectBlock() : basicBlock(0), returnValue(0), contextHolder(0), tempsHolder(0) {} }; struct TPatchInfo { @@ -504,7 +508,7 @@ class JITRuntime { llvm::ExecutionEngine* getExecutionEngine() { return m_executionEngine; } llvm::Module* getModule() { return m_JITModule; } - void optimizeFunction(llvm::Function* function); + void optimizeFunction(llvm::Function* function, bool runModulePass); void printStat(); void initialize(SmalltalkVM* softVM); diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index b83b53c..dffdd88 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -95,7 +95,7 @@ void JITRuntime::printStat() std::printf("\nHot methods:\n"); std::printf("\tHit count\tMethod name\n"); - for (int i = 0; i < 50; i++) { + for (int i = 0; i < 100; i++) { if (hotMethods.empty()) break; @@ -275,12 +275,13 @@ void JITRuntime::updateBlockFunctionCache(TMethod* containerMethod, uint32_t blo } -void JITRuntime::optimizeFunction(Function* function) +void JITRuntime::optimizeFunction(Function* function, bool runModulePass) { - m_modulePassManager->run(*m_JITModule); - // Running the optimization passes on a function m_functionPassManager->run(*function); + + if (runModulePass) + m_modulePassManager->run(*m_JITModule); } TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext) @@ -309,11 +310,11 @@ TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext) std::exit(1); } - outs() << *blockFunction << "\n"; +// outs() << *blockFunction << "\n"; verifyModule(*m_JITModule, AbortProcessAction); - optimizeFunction(blockFunction); + optimizeFunction(blockFunction, true); } compiledBlockFunction = reinterpret_cast(m_executionEngine->getPointerToFunction(blockFunction)); @@ -362,11 +363,11 @@ TObject* JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TOb // Compiling function and storing it to the table for further use methodFunction = m_methodCompiler->compileMethod(method); - outs() << *methodFunction << "\n"; +// outs() << *methodFunction << "\n"; verifyModule(*m_JITModule, AbortProcessAction); - optimizeFunction(methodFunction); + optimizeFunction(methodFunction, true); } // Calling the method and returning the result @@ -379,7 +380,7 @@ TObject* JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TOb } // Updating call site statistics and scheduling method processing - updateHotSites(compiledMethodFunction, callingContext, message, receiverClass, callSiteIndex); + updateHotSites(compiledMethodFunction, previousContext, message, receiverClass, callSiteIndex); // Preparing the context objects. Because we do not call the software // implementation here, we do not need to allocate the stack object @@ -421,6 +422,9 @@ void JITRuntime::updateHotSites(TMethodFunction methodFunction, TContext* callin TMethodFunction callerMethodFunction = lookupFunctionInCache(callingContext->method); // TODO reload cache if callerMethodFunction was popped out + if (!callerMethodFunction) + return; + THotMethod& callerMethod = m_hotMethods[callerMethodFunction]; TCallSite& callSite = callerMethod.callSites[callSiteIndex]; @@ -451,7 +455,7 @@ void JITRuntime::patchHotMethods() outs() << "Patching active methods that have hot call sites\n"; // Processing 50 most active methods - for (uint32_t i = 0, j = hotMethods.size()-1; (i < 50) && (i < hotMethods.size()); i++, j--) { + for (uint32_t i = 0, j = hotMethods.size()-1; /*(i < 50) &&*/ (i < hotMethods.size()); i++, j--) { THotMethod* hotMethod = hotMethods[j]; // We're interested only in methods with call sites @@ -492,7 +496,7 @@ void JITRuntime::patchHotMethods() } // Running optimization passes on functions - for (uint32_t i = 0, j = hotMethods.size()-1; (i < 50) && (i < hotMethods.size()); i++, j--) { + for (uint32_t i = 0, j = hotMethods.size()-1; /*(i < 50) &&*/ (i < hotMethods.size()); i++, j--) { THotMethod* hotMethod = hotMethods[j]; // We're interested only in methods with call sites @@ -505,7 +509,7 @@ void JITRuntime::patchHotMethods() continue; outs() << "Optimizing " << hotMethod->methodFunction->getName().str() << " ..."; - optimizeFunction(hotMethod->methodFunction); + optimizeFunction(hotMethod->methodFunction, false); outs() << "done. Verifying ..."; @@ -516,8 +520,12 @@ void JITRuntime::patchHotMethods() outs() << "done.\n"; } + outs() << "Inlining methods..."; + m_modulePassManager->run(*m_JITModule); + outs() << "done.\n"; + // Compiling functions - for (uint32_t i = 0, j = hotMethods.size()-1; (i < 50) && (i < hotMethods.size()); i++, j--) { + for (uint32_t i = 0, j = hotMethods.size()-1; /*(i < 50) &&*/ (i < hotMethods.size()); i++, j--) { THotMethod* hotMethod = hotMethods[j]; // We're interested only in methods with call sites @@ -573,13 +581,19 @@ void JITRuntime::createDirectBlocks(TPatchInfo& info, TCallSite& callSite, TDire // FIXME Probably we need to select only N most active class hits for (TCallSite::TClassHitsMap::iterator iClassHit = classHits.begin(); iClassHit != classHits.end(); ++iClassHit) { + // Locating a method suitable for a direct call + TMethod* directMethod = m_softVM->lookupMethod(callSite.messageSelector, iClassHit->first); // TODO check for 0 + if (!directMethod) { + outs() << "Lookup failed for handler of " << callSite.messageSelector->toString() << " starting at " << iClassHit->first->name->toString() << "\n"; + directBlocks[iClassHit->first] = TDirectBlock(); + continue; + } + TDirectBlock newBlock; newBlock.basicBlock = BasicBlock::Create(m_JITModule->getContext(), "direct.", info.callInstruction->getParent()->getParent(), info.nextBlock); builder.SetInsertPoint(newBlock.basicBlock); - // Locating a method suitable for a direct call - TMethod* directMethod = m_softVM->lookupMethod(callSite.messageSelector, iClassHit->first); // TODO check for 0 std::string directFunctionName = directMethod->klass->name->toString() + ">>" + callSite.messageSelector->toString(); Function* directFunction = m_JITModule->getFunction(directFunctionName); @@ -789,7 +803,7 @@ void JITRuntime::patchCallSite(llvm::Function* methodFunction, llvm::Value* cont originBlock->getInstList().pop_back(); // Checking whether we may perform constant class propagation - bool isLiteralReceiver = detectLiteralReceiver(info.messageArguments) && (directBlocks.size() == 1); + const bool isLiteralReceiver = false; // detectLiteralReceiver(info.messageArguments) && (directBlocks.size() == 1); if (! isLiteralReceiver) { // Fallback block contains original call to default JIT sendMessage handler. @@ -846,6 +860,12 @@ void JITRuntime::patchCallSite(llvm::Function* methodFunction, llvm::Value* cont } for (TDirectBlockMap::iterator iBlock = directBlocks.begin(); iBlock != directBlocks.end(); ++iBlock) { + if (! iBlock->second.basicBlock) { + outs() << "Skipping malformed direct block, selector " << callSite.messageSelector->toString() + << " class " << iBlock->first->name->toString() << " call site index " << callSiteIndex << "\n"; + continue; + } + TClass* klass = iBlock->first; TDirectBlock& directBlock = iBlock->second; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index f9d3212..cf037e7 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -257,9 +257,9 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) if (st::InstructionNode* const candidate = node->cast()) { if (candidate->getInstruction().mayCauseGC()) { - outs() << "Producer " << producer->getIndex() - << " should be protected because node " - << candidate->getIndex() << " may cause GC\n"; +// outs() << "Producer " << producer->getIndex() +// << " should be protected because node " +// << candidate->getIndex() << " may cause GC\n"; return true; } @@ -295,8 +295,8 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) walker.run(consumer, st::GraphWalker::wdBackward); if (detector.isDetected()) { - outs() << "Producer " << producer->getIndex() - << " should be protected because detector says that it is required\n"; +// outs() << "Producer " << producer->getIndex() +// << " should be protected because detector says that it is required\n"; return true; } @@ -412,7 +412,7 @@ void MethodCompiler::scanForBranches(TJITContext& jit, st::ParsedBytecode* sourc visitor.run(); } -Value* MethodCompiler::createArray(TJITContext& jit, uint32_t elementsCount) +TObjectAndSize MethodCompiler::createArray(TJITContext& jit, uint32_t elementsCount) { TStackObject array = allocateStackObject(*jit.builder, sizeof(TObjectArray), elementsCount); // Instantinating new array object @@ -430,7 +430,7 @@ Value* MethodCompiler::createArray(TJITContext& jit, uint32_t elementsCount) jit.builder->CreateCall2(m_baseFunctions.setObjectSize, arrayObject, jit.builder->getInt32(elementsCount)); jit.builder->CreateCall2(m_baseFunctions.setObjectClass, arrayObject, m_globals.arrayClass); - return arrayObject; + return std::make_pair(arrayObject, arraySize); } Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodFunction /*= 0*/, llvm::Value** contextHolder /*= 0*/) @@ -807,7 +807,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) // outs() << *blockContext.function << "\n"; // Running optimization passes on a block function - JITRuntime::Instance()->optimizeFunction(blockContext.function); + JITRuntime::Instance()->optimizeFunction(blockContext.function, false); // outs() << *blockContext.function << "\n"; } @@ -862,7 +862,9 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) // FIXME Probably we may unroll the arguments array and pass the values directly. // However, in some cases this may lead to additional architectural problems. - Value* const argumentsObject = createArray(jit, argumentsCount); + TObjectAndSize array = createArray(jit, argumentsCount); + Value* const argumentsObject = array.first; + const uint32_t sizeInBytes = array.second; // Filling object with contents uint8_t index = argumentsCount; @@ -872,8 +874,15 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), argument); } Value* const argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); + //Value* const argsHolder = protectProducerNode(jit, jit.currentNode, argumentsArray); + //argsHolder->setName("pArgs."); + + Value* const argumentsPointer = jit.builder->CreateBitCast(argumentsObject, jit.builder->getInt8PtrTy()); + Function* gcrootIntrinsic = getDeclaration(m_JITModule, Intrinsic::lifetime_start); + jit.builder->CreateCall2(gcrootIntrinsic, jit.builder->getInt64(sizeInBytes), argumentsPointer); setNodeValue(jit, jit.currentNode, argumentsArray); + // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); } void MethodCompiler::doSendUnary(TJITContext& jit) @@ -1062,7 +1071,9 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // Then send the message just like ordinary one // Now creating the argument array - Value* argumentsObject = createArray(jit, 2); + TObjectAndSize array = createArray(jit, 2); + Value* const argumentsObject = array.first; + const uint32_t sizeInBytes = array.second; // TODO lifetime jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(0), leftValue); jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(1), rightValue); @@ -1108,7 +1119,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) } -bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode) +bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode, TClass* receiverClass /*= 0*/) { // Optimized version of doSendMessage which takes into account that // pending message should be sent to the literal receiver @@ -1122,28 +1133,32 @@ bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNod TSymbolArray& literals = *jit.originMethod->literals; TSymbol* const messageSelector = literals[jit.currentNode->getInstruction().getArgument()]; - TObject* literalReceiver = 0; - // Determining receiver class - const st::TSmalltalkInstruction::TOpcode opcode = receiverNode->getInstruction().getOpcode(); - if (opcode == opcode::pushLiteral) { - literalReceiver = literals[receiverNode->getInstruction().getArgument()]; - } else if (opcode == opcode::pushConstant) { - const uint8_t constant = receiverNode->getInstruction().getArgument(); - switch(constant) { - case 0: case 1: case 2: case 3: case 4: - case 5: case 6: case 7: case 8: case 9: - literalReceiver = TInteger(constant); - break; - - case pushConstants::nil: literalReceiver = globals.nilObject; break; - case pushConstants::trueObject: literalReceiver = globals.trueObject; break; - case pushConstants::falseObject: literalReceiver = globals.falseObject; break; + if (!receiverClass) { + TObject* literalReceiver = 0; + + const st::TSmalltalkInstruction::TOpcode opcode = receiverNode->getInstruction().getOpcode(); + if (opcode == opcode::pushLiteral) { + literalReceiver = literals[receiverNode->getInstruction().getArgument()]; + } else if (opcode == opcode::pushConstant) { + const uint8_t constant = receiverNode->getInstruction().getArgument(); + switch(constant) { + case 0: case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: case 9: + literalReceiver = TInteger(constant); + break; + + case pushConstants::nil: literalReceiver = globals.nilObject; break; + case pushConstants::trueObject: literalReceiver = globals.trueObject; break; + case pushConstants::falseObject: literalReceiver = globals.falseObject; break; + } } + + assert(literalReceiver); + receiverClass = isSmallInteger(literalReceiver) ? globals.smallIntClass : literalReceiver->getClass(); } - assert(literalReceiver); - TClass* const receiverClass = isSmallInteger(literalReceiver) ? globals.smallIntClass : literalReceiver->getClass(); + assert(receiverClass); // Locating a method suitable for a direct call TMethod* const directMethod = m_runtime.getVM()->lookupMethod(messageSelector, receiverClass); @@ -1162,7 +1177,7 @@ bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNod verifyFunction(*directFunction , llvm::AbortProcessAction); - m_runtime.optimizeFunction(directFunction); + m_runtime.optimizeFunction(directFunction, false); } // Allocating context object and temporaries on the methodFunction's stack. @@ -1257,6 +1272,11 @@ bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNod result = jit.builder->CreateCall(directFunction, newContext); } + AllocaInst* const allocaInst = dyn_cast(jit.currentNode->getArgument()->getValue()->stripPointerCasts()); + Function* gcrootIntrinsic = getDeclaration(m_JITModule, Intrinsic::lifetime_end); + Value* const argumentsPointer = jit.builder->CreateBitCast(arguments, jit.builder->getInt8PtrTy()); + jit.builder->CreateCall2(gcrootIntrinsic, jit.builder->CreateZExt(allocaInst->getArraySize(), jit.builder->getInt64Ty()), argumentsPointer); + Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); setNodeValue(jit, jit.currentNode, resultHolder); @@ -1272,14 +1292,31 @@ void MethodCompiler::doSendMessage(TJITContext& jit) st::InstructionNode* const receiverNode = markArgumentsNode->getArgument()->cast(); assert(receiverNode); + const st::TSmalltalkInstruction instruction = receiverNode->getInstruction(); + // In case of a literal receiver we may encode direct method call - if (receiverNode->getInstruction().getOpcode() == opcode::pushLiteral || - receiverNode->getInstruction().getOpcode() == opcode::pushConstant) + if (instruction.getOpcode() == opcode::pushLiteral || + instruction.getOpcode() == opcode::pushConstant) { if (doSendMessageToLiteral(jit, receiverNode)) return; } + // We may optimize send to self if clas does not have children + // TODO More optimization possible: send to super, if chiildren do not override selector + if (instruction.getOpcode() == opcode::pushArgument && instruction.getArgument() == 0) + { + TClass* const receiverClass = jit.originMethod->klass; + +// TODO if (sendToSuper) +// receiverClass = receiverClass->parentClass; + + if (receiverClass->package == globals.nilObject) { + if (doSendMessageToLiteral(jit, receiverNode, receiverClass)) + return; + } + } + Value* const arguments = getArgument(jit); // jit.popValue(); // First of all we need to get the actual message selector @@ -1318,6 +1355,11 @@ void MethodCompiler::doSendMessage(TJITContext& jit) result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); } + AllocaInst* const allocaInst = dyn_cast(jit.currentNode->getArgument()->getValue()->stripPointerCasts()); + Function* gcrootIntrinsic = getDeclaration(m_JITModule, Intrinsic::lifetime_end); + Value* const argumentsPointer = jit.builder->CreateBitCast(arguments, jit.builder->getInt8PtrTy()); + jit.builder->CreateCall2(gcrootIntrinsic, jit.builder->CreateZExt(allocaInst->getArraySize(), jit.builder->getInt64Ty()), argumentsPointer); + Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); setNodeValue(jit, jit.currentNode, resultHolder); // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); @@ -1850,7 +1892,10 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, default: { // Here we need to create the arguments array from the values on the stack const uint8_t argumentsCount = jit.currentNode->getInstruction().getArgument(); - Value* const argumentsObject = createArray(jit, argumentsCount); + + TObjectAndSize array = createArray(jit, argumentsCount); + Value* const argumentsObject = array.first; + const uint32_t sizeInBytes = array.second; // TODO lifetime // Filling object with contents uint8_t index = argumentsCount; From 1c7e6833aca1392db381416ca80201d4df7f701e Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 8 Mar 2015 23:43:52 +0600 Subject: [PATCH 157/290] Comments out compilation trace messages Issue: #32 --- src/MethodCompiler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index cf037e7..f5081a9 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -440,7 +440,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Creating the function named as "Class>>method" or using provided one jit.function = methodFunction ? methodFunction : createFunction(method); - outs() << "Compiling " << jit.function->getName() << "\n"; +// outs() << "Compiling " << jit.function->getName() << "\n"; // { // std::ostringstream ss; @@ -498,7 +498,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF m_blockFunctions.clear(); // m_targetToBlockMap.clear(); - outs() << "Done compiling method " << jit.function->getName() << "\n"; +// outs() << "Done compiling method " << jit.function->getName() << "\n"; return jit.function; } @@ -762,7 +762,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) ss << jit.originMethod->klass->name->toString() + ">>" + jit.originMethod->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); - outs() << "Compiling block " << blockFunctionName << "\n"; +// outs() << "Compiling block " << blockFunctionName << "\n"; // { // std::ostringstream ss; @@ -812,7 +812,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) // outs() << *blockContext.function << "\n"; } - outs() << "Done compiling block " << blockFunctionName << "\n"; +// outs() << "Done compiling block " << blockFunctionName << "\n"; // Create block object and fill it with context information Value* args[] = { From e39fb2cf79ad35c953ff4a4db00f41adb6734158 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 8 Mar 2015 23:47:04 +0600 Subject: [PATCH 158/290] Adds benchmark which compares various operations in soft and JIT Issue: #32 --- image/imageSource.st | 187 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 158 insertions(+), 29 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index 8aeb582..f33d466 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -1457,6 +1457,13 @@ millisecondsToRun: aBlock | timer | ^timer value. ! +METHOD Timer +millisecondsToRun: aBlock | time | + time <- self value. + aBlock value. + ^self value - time. +! + METHOD Timer initValue ^initValue @@ -1735,8 +1742,8 @@ rebuildImage | classes methods | METHOD MetaSystem rebuildMethod: aMethod for: aClass - "'Compiling ' print. aClass print. ':' print. - aMethod name print. '... ' printNl." + 'Compiling ' print. aClass print. ':' print. + aMethod name print. '... ' printNl. ^aClass parseMethod: aMethod text. ! @@ -1812,37 +1819,153 @@ factTest: x ! METHOD Undefined -main | command data x | - Char initialize. - Class fillChildren. - System fixMethodClasses. - "Jit do: [ nil inlineTest ]." +factTest2: x + (x = 1) ifTrue: [^1] ifFalse: [^self factTest2: x - 1] +! + +CLASS Benchmark Object +CLASS Mock Object + +METHOD Mock +selfReturn + ^self +! + +METHOD Mock +stackReturn + ^nil +! + +METHOD Mock +argReturn: arg + ^arg +! + +METHOD Mock +literalReturn + ^'Hello world!' +! + +METHOD Mock +blockReturn + [^self] value. +! + +METHOD Mock +invokeBlock: aBlock + aBlock value. +! + +METHOD Mock +selfSend + self selfReturn. +! + +METHOD Mock +invokePrimitive + <1 nil nil>. +! + +METHOD Mock +failPrimitive + <21 'Hello' 9>. +! - "Scheduler initialize. - Thread run: [ 1 to: 10 do: [ :x | 'A' printNl ] ]. - Thread run: [ 1 to: 10 do: [ :x | 'B' printNl ] ]. - Scheduler run." +METHOD Mock +doesNotUnderstand: aSelector + ^self +! + +METHOD Mock +invokeUnknownSelector + ^Mock new anUnknownSelector. +! + +METHOD Benchmark +softRun: aBlock rounds: rounds text: text + | time delta | + + System collectGarbage. + + " Executing a block measuring total time " + time <- Timer get. + 1 to: rounds do: [ :x | aBlock value ]. + delta <- Timer get - time. + + text print. $ print. delta printNl. + ^delta +! + +METHOD Benchmark +jitRun: aBlock rounds: rounds text: text + | time delta x | + + System collectGarbage. + time <- Timer get. + + " Executing a block that will do the job " + Jit sendMessage: #value withArguments: + (Array with: [ x <- 0. + [x < rounds] whileTrue: + [ aBlock value. x <- x + 1 ] ]). - "'Soft run: ' print. (Timer millisecondsToRun: [ System rebuildImage ]) printNl." - "'Cold jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl." - "'Hot jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl." + delta <- Timer get - time. + + text print. $ print. delta print. ' (jit)' printNl. + ^delta +! + +METHOD Benchmark +run: aBlock rounds: rounds text: text + self softRun: aBlock rounds: rounds text: text. + self jitRun: aBlock rounds: rounds text: text. +! - "Jit patchHotMethods. +METHOD Benchmark +run: rounds | mock | + mock <- Mock new. - 'Patched cold jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl. - 'Patched hot jit run: ' print. (Timer millisecondsToRun: [ Jit do: [ System rebuildImage ] ]) printNl." + self run: [ 2 + 3 ] rounds: rounds text: 'Literal addition'. + self run: [ 2 + (mock argReturn: 3) ] rounds: rounds text: 'SmallInt + object'. + self run: [ (mock argReturn: 3) + 2] rounds: rounds text: 'object + SmallInt'. - "1 to: 100 do: [ :x | Jit do: [ nil loopBenchmark ] ]." + self run: [ 1 to: rounds do: [ :x | 2 + 3 ] ] rounds: 1 text: 'Literal addition in a loop'. + self run: [ 1 to: rounds do: [ :x | 2 + (mock argReturn: 3) ] ] rounds: 1 text: 'SmallInt + object in a loop'. + self run: [ 1 to: rounds do: [ :x | (mock argReturn: 3) + 2 ] ] rounds: 1 text: 'object + SmallInt in a loop'. - "Jit do: [ List with: 42 with: 43; sort ]." + self run: [ mock selfReturn ] rounds: rounds text: 'Self return'. + self run: [ mock stackReturn ] rounds: rounds text: 'Stack return'. + self run: [ mock argReturn: nil ] rounds: rounds text: 'Arg return'. + self run: [ mock literalReturn ] rounds: rounds text: 'Literal return'. - "Jit do: [ nil runSort: 10000 ]." - "Jit do: [ 'hello' printNl ]." - "Jit do: [ System rebuildImage ]. - Jit printStatistics. - Jit patchHotMethods." - "Jit do: [ [ :p | x <- p ] value: 42 ]." - "Jit do: [ SmallIntTest new runAll ]." + self run: [ mock invokeBlock: [nil] ] rounds: rounds text: 'Block invoke'. + self run: [ mock blockReturn ] rounds: rounds text: 'Block return'. + + self run: [ 'Hello world' size ] rounds: rounds text: 'Literal receiver'. + self run: [ mock selfSend ] rounds: rounds text: 'Self dispatch'. + + " Stupid imageBuilder crashes if method is too long :( " + self run2: rounds +! + +METHOD Benchmark +run2: rounds | mock | + mock <- Mock new. + + self run: [ mock selfReturn selfReturn selfReturn selfReturn selfReturn ] rounds: rounds text: 'Chain dispatch'. + self run: [ mock selfReturn; selfReturn; selfReturn; selfReturn; selfReturn ] rounds: rounds text: 'Cascade dispatch'. + + self run: [ mock invokePrimitive ] rounds: rounds text: 'Primitive invocation'. + self run: [ mock failPrimitive ] rounds: rounds text: 'Failed primitive invocation'. + + self run: [ mock invokeUnknownSelector ] rounds: rounds text: 'Dispatch of #doesNotUnderstand'. +! + +METHOD Undefined +main | command data x | + Char initialize. + Class fillChildren. + System fixMethodClasses. [ command <- String readline: '->'. command notNil ] whileTrue: [ command isEmpty ifFalse: [ command doIt printNl ] ] @@ -5129,9 +5252,15 @@ sendMessage: encoder block: inBlock encoder genHigh: 9 low: (encoder genLiteral: name) ! METHOD MessageNode -argumentsAreBlock - arguments do: [ :arg | arg isBlock ifFalse: [ ^ false ]]. - ^ true +argumentsAreBlock |result| + "Version using block return" + arguments do: [ :arg | arg isBlock ifFalse: [ ^false ] ]. + ^true + + "Linear version" + "result <- true. + arguments do: [ :arg | arg isBlock ifFalse: [ result <- false ] ]. + ^result" ! METHOD MessageNode optimizeWhile: encoder block: inBlock | start save | From 0dc2dc9ad7aff337355f1448e3b4df5792eca419 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 9 Mar 2015 11:03:29 +0600 Subject: [PATCH 159/290] Adds more benchmarks, simplifies TVMExecutionContext::stackPush() Issue: #32 --- image/imageSource.st | 35 +++++++++++++++++++++++++++-------- src/vm.cpp | 14 ++++++++------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index f33d466..229b091 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -1824,7 +1824,7 @@ factTest2: x ! CLASS Benchmark Object -CLASS Mock Object +CLASS Mock Object field METHOD Mock selfReturn @@ -1846,6 +1846,11 @@ literalReturn ^'Hello world!' ! +METHOD Mock +fieldReturn + ^field +! + METHOD Mock blockReturn [^self] value. @@ -1881,6 +1886,17 @@ invokeUnknownSelector ^Mock new anUnknownSelector. ! +METHOD Mock +setField: value + field <- value +! + +METHOD Mock +setTemporary: value | temp | + temp <- value + ^temp +! + METHOD Benchmark softRun: aBlock rounds: rounds text: text | time delta | @@ -1898,16 +1914,14 @@ softRun: aBlock rounds: rounds text: text METHOD Benchmark jitRun: aBlock rounds: rounds text: text - | time delta x | + | time delta | System collectGarbage. time <- Timer get. " Executing a block that will do the job " Jit sendMessage: #value withArguments: - (Array with: [ x <- 0. - [x < rounds] whileTrue: - [ aBlock value. x <- x + 1 ] ]). + (Array with: [ 1 to: rounds do: [ :x | aBlock value ] ]). delta <- Timer get - time. @@ -1937,12 +1951,12 @@ run: rounds | mock | self run: [ mock stackReturn ] rounds: rounds text: 'Stack return'. self run: [ mock argReturn: nil ] rounds: rounds text: 'Arg return'. self run: [ mock literalReturn ] rounds: rounds text: 'Literal return'. + self run: [ mock fieldReturn ] rounds: rounds text: 'Field return'. - self run: [ mock invokeBlock: [nil] ] rounds: rounds text: 'Block invoke'. - self run: [ mock blockReturn ] rounds: rounds text: 'Block return'. + self run: [ mock setField: nil ] rounds: rounds text: 'Set field'. + self run: [ mock setTemporary: nil ] rounds: rounds text: 'Set temporary'. self run: [ 'Hello world' size ] rounds: rounds text: 'Literal receiver'. - self run: [ mock selfSend ] rounds: rounds text: 'Self dispatch'. " Stupid imageBuilder crashes if method is too long :( " self run2: rounds @@ -1952,6 +1966,11 @@ METHOD Benchmark run2: rounds | mock | mock <- Mock new. + self run: [ mock selfSend ] rounds: rounds text: 'Self dispatch'. + + self run: [ mock invokeBlock: [nil] ] rounds: rounds text: 'Block invoke'. + self run: [ mock blockReturn ] rounds: rounds text: 'Block return'. + self run: [ mock selfReturn selfReturn selfReturn selfReturn selfReturn ] rounds: rounds text: 'Chain dispatch'. self run: [ mock selfReturn; selfReturn; selfReturn; selfReturn; selfReturn ] rounds: rounds text: 'Cascade dispatch'. diff --git a/src/vm.cpp b/src/vm.cpp index 1329fd4..6316e8f 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -140,13 +140,15 @@ void SmalltalkVM::TVMExecutionContext::stackPush(TObject* object) uint32_t stackSize = currentContext->stack->getSize(); if( stackTop >= stackSize ) { //resize the current stack - hptr newStack = m_vm->newObject(stackSize+5); - for(uint32_t i = 0; i < stackSize; i++) { - TObject* value = currentContext->stack->getField(i); - newStack->putField(i, value); - } + hptr newStack = m_vm->newObject(stackSize*2); + std::copy(newStack->getFields(), newStack->getFields()+stackSize, currentContext->stack->getFields()); +// for(uint32_t i = 0; i < stackSize; i++) { +// TObject* value = currentContext->stack->getField(i); +// newStack->putField(i, value); +// } currentContext->stack = newStack; - std::cerr << std::endl << "VM: Stack overflow in '" << currentContext->method->name->toString() << "'" << std::endl; + std::cerr << currentContext->method->name->toString() << "!"; + //std::cerr << std::endl << "VM: Stack overflow in '" << currentContext->method->name->toString() << "'" << std::endl; } } currentContext->stack->putField(stackTop++, object); From 6dcc1424bbd992c0c46813808821108324dcd3a0 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Mon, 9 Mar 2015 18:39:47 +0600 Subject: [PATCH 160/290] Adds Array, OrderedArray and List benchmarks Issue: #32 --- image/imageSource.st | 59 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index 229b091..4648409 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -1917,12 +1917,10 @@ jitRun: aBlock rounds: rounds text: text | time delta | System collectGarbage. - time <- Timer get. " Executing a block that will do the job " - Jit sendMessage: #value withArguments: - (Array with: [ 1 to: rounds do: [ :x | aBlock value ] ]). - + time <- Timer get. + Jit do: [ 1 to: rounds do: [ :x | aBlock value ] ]. delta <- Timer get - time. text print. $ print. delta print. ' (jit)' printNl. @@ -1978,6 +1976,49 @@ run2: rounds | mock | self run: [ mock failPrimitive ] rounds: rounds text: 'Failed primitive invocation'. self run: [ mock invokeUnknownSelector ] rounds: rounds text: 'Dispatch of #doesNotUnderstand'. + + self run3: rounds +! + +METHOD Benchmark +run3: rounds | array ordered indices | + array <- Array new: 10000. + indices <- Array new: 10000. + 1 to: array size do: [ :x | indices at: x put: (array size atRandom) ]. + + self run: [ 1 to: array size do: [ :x | array at: x ] ] rounds: rounds / (array size) text: 'Array, linear read'. + self run: [ 1 to: array size do: [ :x | array at: (indices at: x) ] ] rounds: rounds / (array size) text: 'Array, random read'. + + array <- Array new: 2. + self softRun: [ array insert: nil at: array size / 2 ] rounds: rounds text: 'Array, middle insert'. + + array <- Array new: 2. + self jitRun: [ array insert: nil at: array size / 2 ] rounds: rounds text: 'Array, middle insert'. + + ordered <- OrderedArray new: 0. + self softRun: [ 1 to: (indices size) do: [ :x | ordered add: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, creation'. + self softRun: [ 1 to: (indices size) do: [ :x | ordered includes: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, selection'. + + ordered <- OrderedArray new: 0. + self jitRun: [ 1 to: (indices size) do: [ :x | ordered add: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, creation'. + self jitRun: [ 1 to: (indices size) do: [ :x | ordered includes: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, selection'. + + self run4: rounds +! + +METHOD Benchmark +run4: rounds | list | + list <- List new. + self softRun: [ 1 to: rounds do: [ :x | list add: x ] ] rounds: 1 text: 'List front insert'. + + list <- List new. + self jitRun: [ 1 to: rounds do: [ :x | list add: x ] ] rounds: 1 text: 'List front insert'. + + "list <- List new. + self jitRun: [ 1 to: rounds do: [ :x | list addLast: x ] ] rounds: 1 text: 'List back insert'. + + list <- List new. + self jitRun: [ 1 to: rounds do: [ :x | list addLast: x ] ] rounds: 1 text: 'List back insert'." ! METHOD Undefined @@ -2558,7 +2599,7 @@ at: value ! METHOD Collection includes: aValue - self do: [ :element | element = aValue ifTrue: [ ^ true ]]. + self do: [ :element | element = aValue ifTrue: [ ^ true ] ]. ^ false ! METHOD Collection @@ -2841,16 +2882,16 @@ METHOD Array < arg | selfsize argsize | selfsize <- self size. argsize <- arg size. 1 to: (selfsize min: argsize) - do: [:i | (self at: i) ~= (arg at: i) - ifTrue: [ ^ (self at: i) < (arg at: i) ]]. + do: [ :i | (self at: i) ~= (arg at: i) + ifTrue: [ ^ (self at: i) < (arg at: i) ] ]. ^ selfsize < argsize ! METHOD Array = anArray self size = anArray size ifFalse: [ ^ false ]. 1 to: self size do: - [:i | (self at: i) = (anArray at: i) - ifFalse: [ ^ false ]]. + [ :i | (self at: i) = (anArray at: i) + ifFalse: [ ^ false ] ]. ^ true ! METHOD Array From 8167590f796fdb923570be1a0d1c9af91be4dc68 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 18 Mar 2015 20:38:00 +0600 Subject: [PATCH 161/290] Adds statistics of blockReturn invokation Issue: #32 --- include/jit.h | 2 ++ src/JITRuntime.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/jit.h b/include/jit.h index 2d952ca..a9694c9 100644 --- a/include/jit.h +++ b/include/jit.h @@ -400,6 +400,7 @@ class JITRuntime { friend TObject* sendMessage(TContext* callingContext, TSymbol* message, TObjectArray* arguments, TClass* receiverClass, uint32_t callSiteIndex); friend TBlock* createBlock(TContext* callingContext, uint8_t argLocation, uint16_t bytePointer); friend TObject* invokeBlock(TBlock* block, TContext* callingContext); + friend void emitBlockReturn(TObject* value, TContext* targetContext); struct TFunctionCacheEntry { @@ -425,6 +426,7 @@ class JITRuntime { uint32_t m_blockCacheMisses; uint32_t m_messagesDispatched; uint32_t m_blocksInvoked; + uint32_t m_blockReturnsEmitted; uint32_t m_objectsAllocated; TMethodFunction lookupFunctionInCache(TMethod* method); diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index dffdd88..c5affcf 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -76,12 +76,13 @@ void JITRuntime::printStat() "\tMessages dispatched: %12d\n" "\tObjects allocated: %12d\n" "\tBlocks invoked: %12d\n" + "\tBlockReturn emitted: %12d\n" "\tBlock cache hits: %12d misses %10d ratio %6.2f %%\n" "\tMessage cache hits: %12d misses %10d ratio %6.2f %%\n", m_messagesDispatched, m_objectsAllocated, - m_blocksInvoked, + m_blocksInvoked, m_blockReturnsEmitted, m_blockCacheHits, m_blockCacheMisses, blockHitRatio, m_cacheHits, m_cacheMisses, hitRatio ); @@ -191,6 +192,7 @@ void JITRuntime::initialize(SmalltalkVM* softVM) m_cacheMisses = 0; m_messagesDispatched = 0; m_blocksInvoked = 0; + m_blockReturnsEmitted = 0; m_objectsAllocated = 0; } @@ -1101,6 +1103,7 @@ TObject* invokeBlock(TBlock* block, TContext* callingContext) void emitBlockReturn(TObject* value, TContext* targetContext) { + JITRuntime::Instance()->m_blockReturnsEmitted++; throw TBlockReturn(value, targetContext); } From d935529838430af88332eb5d096db093f72cdb91 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 18 Mar 2015 20:38:50 +0600 Subject: [PATCH 162/290] Adds Tree benchmarks --- image/imageSource.st | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index 4648409..7abfca0 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -1996,29 +1996,34 @@ run3: rounds | array ordered indices | self jitRun: [ array insert: nil at: array size / 2 ] rounds: rounds text: 'Array, middle insert'. ordered <- OrderedArray new: 0. - self softRun: [ 1 to: (indices size) do: [ :x | ordered add: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, creation'. - self softRun: [ 1 to: (indices size) do: [ :x | ordered includes: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, selection'. + self softRun: [ 1 to: (indices size) do: [ :x | ordered add: (indices at: x) ] ] rounds: rounds / (indices size) text: 'OrderedArray, creation'. + self softRun: [ 1 to: (indices size) do: [ :x | ordered includes: (indices at: x) ] ] rounds: rounds / (indices size) text: 'OrderedArray, selection'. ordered <- OrderedArray new: 0. - self jitRun: [ 1 to: (indices size) do: [ :x | ordered add: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, creation'. - self jitRun: [ 1 to: (indices size) do: [ :x | ordered includes: (indices at: x) ] ] rounds: 1 text: 'OrderedArray, selection'. + self jitRun: [ 1 to: (indices size) do: [ :x | ordered add: (indices at: x) ] ] rounds: rounds / (indices size) text: 'OrderedArray, creation'. + self jitRun: [ 1 to: (indices size) do: [ :x | ordered includes: (indices at: x) ] ] rounds: rounds / (indices size) text: 'OrderedArray, selection'. self run4: rounds ! METHOD Benchmark -run4: rounds | list | +run4: rounds | list indices tree | list <- List new. self softRun: [ 1 to: rounds do: [ :x | list add: x ] ] rounds: 1 text: 'List front insert'. list <- List new. self jitRun: [ 1 to: rounds do: [ :x | list add: x ] ] rounds: 1 text: 'List front insert'. - "list <- List new. - self jitRun: [ 1 to: rounds do: [ :x | list addLast: x ] ] rounds: 1 text: 'List back insert'. + indices <- Array new: 10000. + 1 to: indices size do: [ :x | indices at: x put: (indices size atRandom) ]. - list <- List new. - self jitRun: [ 1 to: rounds do: [ :x | list addLast: x ] ] rounds: 1 text: 'List back insert'." + tree <- Tree new. + self softRun: [ 1 to: indices size do: [ :x | tree add: (indices at: x) ] ] rounds: 1 text: 'Tree insert'. + + tree <- Tree new. + self jitRun: [ 1 to: indices size do: [ :x | tree add: (indices at: x) ] ] rounds: 1 text: 'Tree insert'. + + self run: [ tree collect: [ :x | x * 2 ] ] rounds: 1 text: 'Tree collect'. ! METHOD Undefined @@ -4156,7 +4161,7 @@ copy METHOD Tree collect: transformBlock | newTree | newTree <- Tree new. - self do: [:element| newTree add: (transformBlock value: element)] + self do: [:element| newTree add: (transformBlock value: element)]. ^newTree ! METHOD Tree From 04c182913475b6b6dcdf33416b45dc27ccb14951 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 19 Mar 2015 20:21:04 +0300 Subject: [PATCH 163/290] Adds basic .travis.yml file Issue: #65 --- .travis.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9b96219 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: cpp + +compiler: + - gcc + - clang + +os: + - linux + +before_script: cmake . + +script: make + From 457bf7b1524f38263e48ee67156c8b9eb6dfc741 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 19 Mar 2015 21:22:22 +0300 Subject: [PATCH 164/290] Adds i386 deps Issue: #65 --- .travis.yml | 6 ++++++ .travis/linux/install-dependencies.sh | 14 ++++++++++++++ 2 files changed, 20 insertions(+) create mode 100755 .travis/linux/install-dependencies.sh diff --git a/.travis.yml b/.travis.yml index 9b96219..e7fa0f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,12 @@ compiler: os: - linux +cache: + apt: true + +before_install: + - ./.travis/linux/install-dependencies.sh + before_script: cmake . script: make diff --git a/.travis/linux/install-dependencies.sh b/.travis/linux/install-dependencies.sh new file mode 100755 index 0000000..2a364cd --- /dev/null +++ b/.travis/linux/install-dependencies.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test +sudo apt-get update -qq + +sudo apt-get install -y binutils:i386 llvm-3.3-dev:i386 libreadline-dev:i386 + +if [ "$CXX" = "clang++" ]; then + sudo apt-get install -y g++-4.8-multilib +fi +if [ "$CXX" = "g++" ]; then + sudo apt-get install -y gcc:i386 g++:i386 cpp:i386 g++-4.6:i386 gcc-4.6:i386 +fi + From 50291301971be73698ec55b57b6bbecf00974e5e Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 20 Mar 2015 01:56:23 +0300 Subject: [PATCH 165/290] Turns on llvm Issue: #65 --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e7fa0f1..7ba6314 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,15 @@ os: cache: apt: true +env: + matrix: + - USE_LLVM=Off + - USE_LLVM=On + before_install: - ./.travis/linux/install-dependencies.sh -before_script: cmake . +before_script: cmake . -DUSE_LLVM=$USE_LLVM -DCMAKE_BUILD_TYPE=Release script: make From 9655ab3bf730ad065c906e25a4a4d9586e1b037a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Mon, 23 Mar 2015 16:24:42 +0300 Subject: [PATCH 166/290] Removes email notifications Issue: #65 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7ba6314..71ac4fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,9 @@ os: cache: apt: true +notifications: + email: false + env: matrix: - USE_LLVM=Off From cd25fc34cdad541f3fa754aba93ed28df54278e4 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Mar 2015 02:37:14 +0300 Subject: [PATCH 167/290] Builds only master, develop or any branch which name contains 'travis' Issue: #65 --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 71ac4fc..61c8d8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,12 @@ cache: notifications: email: false +branches: + only: + - master + - develop + - /^.*travis.*$/ + env: matrix: - USE_LLVM=Off From 152b324089b28e7270a20cb5fbc410179103c433 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 29 Mar 2015 05:09:01 +0300 Subject: [PATCH 168/290] Improves CTest support Issue: #61 --- CMakeLists.txt | 9 +++++++++ CTestConfig.cmake | 6 ++++++ cmake/CTestCustom.cmake.in | 5 +++++ tests/CMakeLists.txt | 15 +++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 CTestConfig.cmake create mode 100644 cmake/CTestCustom.cmake.in create mode 100644 tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 9757e74..bc56ebb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,15 @@ else() endif() +option(BUILD_TESTS "Should we build tests?" OFF) + +if (BUILD_TESTS) + enable_testing() + configure_file( "${CMAKE_SOURCE_DIR}/cmake/CTestCustom.cmake.in" "${CMAKE_BINARY_DIR}/CTestCustom.cmake" @ONLY) + include(CTest) + add_subdirectory(tests) +endif() + add_subdirectory(doc) add_subdirectory(image) add_subdirectory(src) diff --git a/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 0000000..e15f7f2 --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,6 @@ +set(CTEST_PROJECT_NAME "LLST") +set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC") +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=LLST") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/cmake/CTestCustom.cmake.in b/cmake/CTestCustom.cmake.in new file mode 100644 index 0000000..b890495 --- /dev/null +++ b/cmake/CTestCustom.cmake.in @@ -0,0 +1,5 @@ +# Complete list of options is available at http://cmake.org/Wiki/CMake/Testing_With_CTest + +SET(CTEST_CUSTOM_MEMCHECK_IGNORE ${CTEST_CUSTOM_MEMCHECK_IGNORE} ) +SET(CTEST_CUSTOM_COVERAGE_EXCLUDE ${CTEST_CUSTOM_COVERAGE_EXCLUDE} ) +SET(CTEST_CUSTOM_WARNING_EXCEPTION ${CTEST_CUSTOM_WARNING_EXCEPTION} ) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..1a1b8ac --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,15 @@ + +find_package(GTest REQUIRED) + +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) + +macro(cxx_test pretty_name bin_name sources libs) + add_executable(${bin_name} EXCLUDE_FROM_ALL ${sources}) + target_link_libraries(${bin_name} ${libs} ${GTEST_BOTH_LIBRARIES}) + set_target_properties(${bin_name} PROPERTIES COMPILE_DEFINITIONS TESTS_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\") + add_test(${pretty_name} ${bin_name}) + add_dependencies(check ${bin_name}) +endmacro() + +include_directories(${CMAKE_SOURCE_DIR}/include) + From 643228e144c2ee997292eafb551091bbd015d1cb Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 29 Mar 2015 06:32:56 +0300 Subject: [PATCH 169/290] Target `make all` builds tests but does not run it Issue: #61 --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1a1b8ac..51ce859 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(GTest REQUIRED) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) macro(cxx_test pretty_name bin_name sources libs) - add_executable(${bin_name} EXCLUDE_FROM_ALL ${sources}) + add_executable(${bin_name} ${sources}) target_link_libraries(${bin_name} ${libs} ${GTEST_BOTH_LIBRARIES}) set_target_properties(${bin_name} PROPERTIES COMPILE_DEFINITIONS TESTS_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\") add_test(${pretty_name} ${bin_name}) From 3f325f5798877fe20a3acd7785f98cd20567b341 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 10 Apr 2015 02:43:16 +0300 Subject: [PATCH 170/290] Adds more detailed description of cli options Issue: #62 --- doc/llst.1.en.pod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/llst.1.en.pod b/doc/llst.1.en.pod index 84a46f9..6080f41 100644 --- a/doc/llst.1.en.pod +++ b/doc/llst.1.en.pod @@ -25,11 +25,11 @@ B - a SmallTalk virtual machine with JIT based on LLVM. =item B<-h> heap_size, B<--heap=>heap_size - Init size of the VM heap + Init size of the VM heap. If there is no enough space for VM to live in the size will be increased. =item B<-H> max_heap_size, B<--heap_max=>max_heap_size - Max size of the VM heap + Max size of the VM heap. VM will not increase the size of the heap if maximum size is reached. =item B<--help> From e54cafa9f7caed34206d5a56248cd4a43e51ac49 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 10 Apr 2015 02:44:14 +0300 Subject: [PATCH 171/290] Adds basic description of the project Issue: #62 --- doc/llst.1.en.pod | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/llst.1.en.pod b/doc/llst.1.en.pod index 6080f41..4a2a78e 100644 --- a/doc/llst.1.en.pod +++ b/doc/llst.1.en.pod @@ -15,6 +15,12 @@ B [B<-h> heap_size] [B<-H> max_heap_size] [[B<-i>] image] B - a SmallTalk virtual machine with JIT based on LLVM. +First of all you need to build image from *.st with imageBuilder, then you run llst on the image. +*.st - is a SmallTalk file containing the full declaration of an image(e.g. standard classes). +The execution starts from Undefined>>main. + +FIXME: Provide imageBuilder with the package (you may find the binary in sources) + =head2 OPTIONS =over 6 From ce7583311bcdebd2cc2951f79a9b7aee459fe1d0 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 29 Mar 2015 09:46:33 +0300 Subject: [PATCH 172/290] Adds support of ctest+cdash Issue: #65 --- .travis.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 61c8d8f..ae4d544 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,19 @@ env: - USE_LLVM=On before_install: - - ./.travis/linux/install-dependencies.sh + - ./.travis/${TRAVIS_OS_NAME}/install-dependencies.sh -before_script: cmake . -DUSE_LLVM=$USE_LLVM -DCMAKE_BUILD_TYPE=Release +before_script: + - mkdir -p build + - cd build + - cmake . -DUSE_LLVM=$USE_LLVM -DBUILD_TESTS=On -DCMAKE_BUILD_TYPE=Release -DSITE="${TRAVIS_OS_NAME}@Travis" -DBUILDNAME="${TRAVIS_BRANCH}_${CMAKE_CXX_COMPILER}" -script: make +script: + - cd build + - make ExperimentalBuild + - make ExperimentalTest + +after_script: + - cd build + - make ExperimentalSubmit From 9c0bb8ad3105de165879ef45531c3eed0358a120 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 10 Apr 2015 01:53:56 +0300 Subject: [PATCH 173/290] Improves handling of GTest libs when they are missing or when they are of incorrect architecture Issue: #61 --- cmake/GTest.cmake | 80 ++++++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 5 ++- 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 cmake/GTest.cmake diff --git a/cmake/GTest.cmake b/cmake/GTest.cmake new file mode 100644 index 0000000..fd009c0 --- /dev/null +++ b/cmake/GTest.cmake @@ -0,0 +1,80 @@ +# We try to find GTest package, +# if it was not found or we could not be simple test, +# add gtest as external project + +find_package(GTest QUIET) + +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) +include(ExternalProject) +include(FindPackageMessage) + +function(add_gtest_as_external) + set (gtest_local "/usr/src/gtest/") + if (EXISTS "${gtest_local}/CMakeLists.txt") + set (gtest_url URL ${gtest_local}) + FIND_PACKAGE_MESSAGE(GTest "Trying to build GTest from local ${gtest_local}" "[${gtest_local}]") + else() + set (gtest_web_url "https://googletest.googlecode.com/files/gtest-1.7.0.zip") + FIND_PACKAGE_MESSAGE(GTest "Trying to build GTest from ${gtest_web_url}" "[${gtest_web_url}]") + set (gtest_url + URL ${gtest_web_url} + LOG_DOWNLOAD Yes + ) + endif() + + ExternalProject_Add( + gtest-external ${gtest_url} + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gtest + INSTALL_COMMAND "" + CMAKE_ARGS + -Dgtest_disable_pthreads=On + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + # We don't want to check compiler by subproject again + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_COMPILER_ID_RUN=TRUE + -DCMAKE_CXX_COMPILER_WORKS=TRUE + -DCMAKE_CXX_COMPILER_FORCED=TRUE + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_C_COMPILER_ID_RUN=TRUE + -DCMAKE_C_COMPILER_WORKS=TRUE + -DCMAKE_C_COMPILER_FORCED=TRUE + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + ) + ExternalProject_Get_Property(gtest-external source_dir binary_dir) + set(GTEST_INCLUDE_DIR "${source_dir}/include" CACHE PATH "Path to directory." FORCE) + + add_library(gtest IMPORTED STATIC GLOBAL) + add_dependencies(gtest gtest-external) + set(GTEST_LIBRARY gtest CACHE STRING "Imported library." FORCE) + set(GTEST_LIBRARY_DEBUG gtest CACHE STRING "Imported library." FORCE) + set_property(TARGET gtest PROPERTY + IMPORTED_LOCATION "${binary_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") + + add_library(gtest_main IMPORTED STATIC GLOBAL) + add_dependencies(gtest_main gtest-external) + set(GTEST_MAIN_LIBRARY gtest_main CACHE STRING "Imported library." FORCE) + set(GTEST_MAIN_LIBRARY_DEBUG gtest_main CACHE STRING "Imported library." FORCE) + set_property(TARGET gtest_main PROPERTY + IMPORTED_LOCATION "${binary_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + + set(GTEST_BOTH_LIBRARIES "${GTEST_MAIN_LIBRARY}" "${GTEST_LIBRARY}" PARENT_SCOPE) + set(GTEST_FOUND TRUE CACHE INTERNAL "GTEST_FOUND") +endfunction() + +function(try_compile_gtest out_var) + CMAKE_PUSH_CHECK_STATE() + set(CMAKE_REQUIRED_FLAGS -Wl,-verbose) + set(CMAKE_REQUIRED_LIBRARIES ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + CHECK_CXX_SOURCE_COMPILES("#include \n TEST(x,y){}" ${out_var}) + CMAKE_POP_CHECK_STATE() +endfunction() + +if (GTEST_FOUND) + try_compile_gtest(GTEST_COMPILED_AND_LINKED) + if (NOT GTEST_COMPILED_AND_LINKED) + add_gtest_as_external() + endif() +else() + add_gtest_as_external() +endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 51ce859..c371e1d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,4 @@ - -find_package(GTest REQUIRED) +include(GTest) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) @@ -11,5 +10,5 @@ macro(cxx_test pretty_name bin_name sources libs) add_dependencies(check ${bin_name}) endmacro() -include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_SOURCE_DIR}/include ${GTEST_INCLUDE_DIR}) From 52a52c1faf4e6f1069c7a06803c76dd4a0ad8d12 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Apr 2015 02:05:13 +0300 Subject: [PATCH 174/290] Improves error messages in FindLLVM Issue: #61 --- cmake/FindLLVM.cmake | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake index 25d916a..288b68b 100644 --- a/cmake/FindLLVM.cmake +++ b/cmake/FindLLVM.cmake @@ -101,9 +101,6 @@ endif() find_program(LLVM_CONFIG_EXE NAMES ${LLVM_CONFIG_NAMES} DOC "Full path to llvm-config") mark_as_advanced(LLVM_CONFIG_EXE) -if (NOT LLVM_CONFIG_EXE) - message(STATUS "Could NOT find llvm-config (tried ${LLVM_CONFIG_NAMES})") -endif() get_llvm_config_var(--cppflags LLVM_CPP_FLAGS) get_llvm_config_var(--cxxflags LLVM_CXX_FLAGS) @@ -120,18 +117,31 @@ check_llvm_libs(LLVM_LIBS_INSTALLED) check_llvm_header("llvm/Support/TargetSelect.h" LLVM_HEADERS_INSTALLED) check_llvm_source_compiles("#include \n int main(){ return 0; }" LLVM_COMPILED_AND_LINKED) -if (LLVM_HEADERS_INSTALLED AND NOT LLVM_LIBS_INSTALLED) - message(STATUS "Only header files installed in the package") +unset(custom_fail_msg) +set(required_vars LLVM_CONFIG_EXE LLVM_LIBS_INSTALLED LLVM_HEADERS_INSTALLED LLVM_COMPILED_AND_LINKED) +if (NOT LLVM_CONFIG_EXE) + string(REPLACE ";" ", " config_names "${LLVM_CONFIG_NAMES}") + set(custom_fail_msg "Could NOT find llvm-config (tried ${config_names})") + set(required_vars LLVM_CONFIG_EXE) # for a pretty fail message +elseif (LLVM_HEADERS_INSTALLED AND NOT LLVM_LIBS_INSTALLED) + set(custom_fail_msg "Only header files installed in the package") elseif (LLVM_LIBS_INSTALLED AND NOT LLVM_HEADERS_INSTALLED) - message(STATUS "Libs installed while header files are missing") + set(custom_fail_msg "Libs installed while header files are missing") elseif (LLVM_HEADERS_INSTALLED AND LLVM_LIBS_INSTALLED AND NOT LLVM_COMPILED_AND_LINKED) - message(STATUS "Libs and headers are installed, but during test compilation something went wrong. See ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log for details") + set(custom_fail_msg " + LLVM libs and headers are installed, + but during test compilation something went wrong. + It could happen if the package is not i386. + See ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log for details. + ") +else() + set(custom_fail_msg "Only llvm-config is installed") endif() FIND_PACKAGE_HANDLE_STANDARD_ARGS( LLVM - FOUND_VAR LLVM_FOUND - REQUIRED_VARS LLVM_CONFIG_EXE LLVM_LIBS_INSTALLED LLVM_HEADERS_INSTALLED LLVM_COMPILED_AND_LINKED + REQUIRED_VARS ${required_vars} VERSION_VAR LLVM_VERSION + FAIL_MESSAGE "${custom_fail_msg}" ) set(LLVM_FOUND ${LLVM_FOUND} CACHE INTERNAL "LLVM_FOUND") From fccf2e17e51de1c3764b25f7b4ecc86df146c2a3 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 10 Apr 2015 11:59:10 +0300 Subject: [PATCH 175/290] Do not use ppa to satisfy the deps Issue: #65 --- .travis.yml | 9 ++++----- .travis/linux/install-dependencies.sh | 14 ++++++-------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae4d544..80182ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,8 +21,8 @@ branches: env: matrix: - - USE_LLVM=Off - - USE_LLVM=On + - USE_LLVM=Off # if you rename On/Off, + - USE_LLVM=On # fix all scripts (e.g. install-dependencies.sh) before_install: - ./.travis/${TRAVIS_OS_NAME}/install-dependencies.sh @@ -30,14 +30,13 @@ before_install: before_script: - mkdir -p build - cd build - - cmake . -DUSE_LLVM=$USE_LLVM -DBUILD_TESTS=On -DCMAKE_BUILD_TYPE=Release -DSITE="${TRAVIS_OS_NAME}@Travis" -DBUILDNAME="${TRAVIS_BRANCH}_${CMAKE_CXX_COMPILER}" + - cmake .. -DUSE_LLVM=$USE_LLVM -DBUILD_TESTS=On -DCMAKE_BUILD_TYPE=Release -DSITE="${TRAVIS_OS_NAME}@Travis" -DBUILDNAME="${TRAVIS_BRANCH}_${CXX}_LLVM_${USE_LLVM}" script: - - cd build - make ExperimentalBuild - - make ExperimentalTest after_script: - cd build + - make ExperimentalTest - make ExperimentalSubmit diff --git a/.travis/linux/install-dependencies.sh b/.travis/linux/install-dependencies.sh index 2a364cd..06d200d 100755 --- a/.travis/linux/install-dependencies.sh +++ b/.travis/linux/install-dependencies.sh @@ -1,14 +1,12 @@ #!/bin/sh -sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test sudo apt-get update -qq -sudo apt-get install -y binutils:i386 llvm-3.3-dev:i386 libreadline-dev:i386 +alias apt_install='sudo apt-get -qy --no-install-suggests --no-install-recommends install' -if [ "$CXX" = "clang++" ]; then - sudo apt-get install -y g++-4.8-multilib -fi -if [ "$CXX" = "g++" ]; then - sudo apt-get install -y gcc:i386 g++:i386 cpp:i386 g++-4.6:i386 gcc-4.6:i386 -fi +apt_install "g++-multilib" gcc-multilib linux-libc-dev:i386 # 32bit compilers +apt_install libreadline-dev:i386 +if [ "$USE_LLVM" = "On" ]; then + apt_install llvm-3.3:i386 llvm-3.3-dev:i386 libllvm3.3:i386 llvm-3.3-runtime:i386 +fi From 4fdbac88477e62bb7e2512c7c22dfa987f0863bf Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 29 Apr 2014 22:36:32 +0400 Subject: [PATCH 176/290] Builds all graph* and parsed* code into a separate libstapi Issue: #32 --- CMakeLists.txt | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fa54c5..bfe0d7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,15 +79,33 @@ add_subdirectory(src) add_subdirectory(include) include_directories(include) +add_library(stapi + src/TSmalltalkInstruction.cpp + src/InstructionDecoder.cpp + src/ParsedBytecode.cpp + src/ParsedMethod.cpp + src/ParsedBlock.cpp + + src/ControlGraph.cpp + src/ControlGraphVisualizer.cpp +) + +set(MM_CPP_FILES + src/BakerMemoryManager.cpp + src/GenerationalMemoryManager.cpp + src/NonCollectMemoryManager.cpp +) +if (USE_LLVM) + list(APPEND MM_CPP_FILES src/LLVMMemoryManager.cpp) +endif() + +add_library(memory_managers ${MM_CPP_FILES}) + # Base set of sources needed in every build -set(CPP_FILES +add_library(standart_set src/args.cpp - src/BakerMemoryManager.cpp src/CompletionEngine.cpp - src/GenerationalMemoryManager.cpp src/Image.cpp - src/main.cpp - src/NonCollectMemoryManager.cpp src/primitives.cpp src/TDictionary.cpp src/TSymbol.cpp @@ -104,9 +122,7 @@ set(CPP_FILES ) if (USE_LLVM) - # LLVM specific sources - list(APPEND CPP_FILES - src/LLVMMemoryManager.cpp + add_library(jit src/MethodCompiler.cpp src/JITRuntime.cpp src/llstPass.cpp @@ -114,10 +130,14 @@ if (USE_LLVM) ) endif() -add_executable(llst ${CPP_FILES}) -target_link_libraries(llst ${LLVM_LIBS_TO_LINK} ${READLINE_LIBS_TO_LINK} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) +add_executable(llst src/main.cpp src/vm.cpp) +target_link_libraries(llst standart_set memory_managers stapi ${READLINE_LIBS_TO_LINK} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) add_dependencies(llst image) +if (USE_LLVM) + target_link_libraries(llst jit ${LLVM_LIBS} ${LLVM_LD_FLAGS}) +endif() + set(changelog_compressed "${CMAKE_CURRENT_BINARY_DIR}/changelog.gz") gzip_compress("compress_changelog" "${CMAKE_CURRENT_SOURCE_DIR}/ChangeLog" ${changelog_compressed}) From b2c8bc811503ef3fa261a75660ad600e73eed697 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 30 Apr 2014 02:01:58 +0400 Subject: [PATCH 177/290] Add stub for tests Issue: #32 --- CMakeLists.txt | 7 +++++++ tests/CMakeLists.txt | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index bfe0d7c..83a8440 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,13 @@ else() endif() +option(BUILD_TESTS "Should we build tests?" OFF) + +if (BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() + add_subdirectory(doc) add_subdirectory(image) add_subdirectory(src) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..72c6077 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,19 @@ + +find_package(GTest REQUIRED) + +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) + +macro(cxx_test pretty_name bin_name sources libs) + add_executable(${bin_name} EXCLUDE_FROM_ALL ${sources}) + if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + # this is a 64-bit OS + set_target_properties(${bin_name} PROPERTIES + COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") + endif() + set_target_properties(${bin_name} PROPERTIES COMPILE_DEFINITIONS TESTS_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\") + target_link_libraries(${bin_name} ${libs} ${GTEST_BOTH_LIBRARIES} ) + add_test(${pretty_name} ${bin_name}) + add_dependencies(check ${bin_name}) +endmacro() + +include_directories(${CMAKE_SOURCE_DIR}/include) From 87da420d2e760c74c600786f6472f269aff8bf54 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 30 Apr 2014 02:13:42 +0400 Subject: [PATCH 178/290] Adds test for TSmalltalkInstruction Issue: #32 --- tests/CMakeLists.txt | 2 + tests/tsmalltalk_instruction.cpp | 142 +++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 tests/tsmalltalk_instruction.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 72c6077..9f8bb76 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,3 +17,5 @@ macro(cxx_test pretty_name bin_name sources libs) endmacro() include_directories(${CMAKE_SOURCE_DIR}/include) + +cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;-pthread") diff --git a/tests/tsmalltalk_instruction.cpp b/tests/tsmalltalk_instruction.cpp new file mode 100644 index 0000000..5555160 --- /dev/null +++ b/tests/tsmalltalk_instruction.cpp @@ -0,0 +1,142 @@ +#include +#include + +TEST(TSmalltalkInstruction, isTerminator) +{ + { + SCOPED_TRACE("branches are terminators"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branch).isTerminator()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).isTerminator()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).isTerminator()); + } + { + SCOPED_TRACE("returns are terminators"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::stackReturn).isTerminator()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::selfReturn).isTerminator()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).isTerminator()); + } +} + +TEST(TSmalltalkInstruction, isBranch) +{ + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branch).isBranch()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).isBranch()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).isBranch()); +} + +TEST(TSmalltalkInstruction, isValueProvider) +{ + { + SCOPED_TRACE("branches are not providers"); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branch).isValueProvider()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).isValueProvider()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).isValueProvider()); + } + { + SCOPED_TRACE("returns are not providers"); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::stackReturn).isValueProvider()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::selfReturn).isValueProvider()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).isValueProvider()); + } + { + SCOPED_TRACE("some other insts are not providers either"); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignTemporary).isValueProvider()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignInstance).isValueProvider()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doPrimitive).isValueProvider()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::popTop).isValueProvider()); + } +} + +TEST(TSmalltalkInstruction, isValueConsumer) +{ + { + SCOPED_TRACE("conditional branches are consumers"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).isValueConsumer()); + } + { + SCOPED_TRACE("insts dealing with messages are consumers"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::markArguments).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendMessage).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendUnary).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendBinary).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::sendToSuper).isValueConsumer()); + } + { + SCOPED_TRACE("stack readers are consumers"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::assignTemporary).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::assignInstance).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doPrimitive).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::duplicate).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::popTop).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::stackReturn).isValueConsumer()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).isValueConsumer()); + } +} + +TEST(TSmalltalkInstruction, serializeIsInverseToCtor) +{ + using namespace opcode; + static const Opcode opcodes[] = + { + extended, + pushInstance, + pushArgument, + pushTemporary, + pushLiteral, + pushConstant, + assignInstance, + assignTemporary, + markArguments, + sendMessage, + sendUnary, + sendBinary, + pushBlock, + doPrimitive, + doSpecial + }; + for(std::size_t i = 0; i < sizeof(opcodes) / sizeof(opcodes[0]); i++) + { + SCOPED_TRACE(i); + const Opcode currentOpcode = opcodes[i]; + + const st::TSmalltalkInstruction::TArgument minArgument = std::numeric_limits::min(); + const st::TSmalltalkInstruction::TArgument maxArgument = std::numeric_limits::max(); + + const st::TSmalltalkInstruction::TExtra minExtra = std::numeric_limits::min(); + const st::TSmalltalkInstruction::TExtra maxExtra = std::numeric_limits::max(); + + { + SCOPED_TRACE("minArgument;minExtra"); + st::TSmalltalkInstruction x( currentOpcode, minArgument, minExtra ); + st::TSmalltalkInstruction y( x.serialize() ); + EXPECT_EQ(x.getOpcode(), y.getOpcode()); + EXPECT_EQ(x.getArgument(), y.getArgument()); + EXPECT_EQ(x.getExtra(), y.getExtra()); + } + { + SCOPED_TRACE("maxArgument;minExtra"); + st::TSmalltalkInstruction x( currentOpcode, maxArgument, minExtra ); + st::TSmalltalkInstruction y( x.serialize() ); + EXPECT_EQ(x.getOpcode(), y.getOpcode()); + EXPECT_EQ(x.getArgument(), y.getArgument()); + EXPECT_EQ(x.getExtra(), y.getExtra()); + } + { + SCOPED_TRACE("minArgument;maxExtra"); + st::TSmalltalkInstruction x( currentOpcode, minArgument, maxExtra); + st::TSmalltalkInstruction y( x.serialize() ); + EXPECT_EQ(x.getOpcode(), y.getOpcode()); + EXPECT_EQ(x.getArgument(), y.getArgument()); + EXPECT_EQ(x.getExtra(), y.getExtra()); + } + { + SCOPED_TRACE("maxArgument;maxExtra"); + st::TSmalltalkInstruction x( currentOpcode, maxArgument, maxExtra ); + st::TSmalltalkInstruction y( x.serialize() ); + EXPECT_EQ(x.getOpcode(), y.getOpcode()); + EXPECT_EQ(x.getArgument(), y.getArgument()); + EXPECT_EQ(x.getExtra(), y.getExtra()); + } + } +} From 95b8cdf48e11fc238a34d63a2373dc4f49a26f3d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 30 Apr 2014 02:15:53 +0400 Subject: [PATCH 179/290] Adds tests for InstructionDecoder Issue: #32 --- tests/CMakeLists.txt | 1 + tests/instruction_decoder.cpp | 206 ++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 tests/instruction_decoder.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9f8bb76..9387904 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,3 +19,4 @@ endmacro() include_directories(${CMAKE_SOURCE_DIR}/include) cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;-pthread") +cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;-pthread") diff --git a/tests/instruction_decoder.cpp b/tests/instruction_decoder.cpp new file mode 100644 index 0000000..593fa2d --- /dev/null +++ b/tests/instruction_decoder.cpp @@ -0,0 +1,206 @@ +#include +#include + +class InstructionDecoderRealMethodTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + void* mem = calloc(4, 1024); // magic numbers + m_bytecode = new (mem) TByteObject(42, static_cast(0)); + static const uint8_t bytes[] = + { + // This is the bytecode for method Block>>assertEq:withComment: + + 64, // PushLiteral 0 + 129, // MarkArguments 1 + 145, // SendMessage new + 113, // AssignTemporary 1 + 245, // DoSpecial popTop + 49, // PushTemporary 1 + 16, // PushInstance 0 + 129, // MarkArguments 1 + 146, // SendMessage name + 129, // MarkArguments 1 + 147, // SendMessage printString + 178, // SendBinary + + 113, // AssignTemporary 1 + 245, // DoSpecial popTop + 34, // PushArgument 2 + 161, // SendUnary notNil + 248, //// DoSpecial branchIfFalse 30 + 30, /// + 0, // + 49, // PushTemporary 1 + 68, // PushLiteral 4 + 178, // SendBinary + + 34, // PushArgument 2 + 178, // SendBinary + + 69, // PushLiteral 5 + 178, // SendBinary + + 113, // AssignTemporary 1 + 246, //// DoSpecial branch 31 + 31, /// + 0, // + 90, // PushConstant nil + 245, // DoSpecial popTop + 49, // PushTemporary 1 + 129, // MarkArguments 1 + 150, // SendMessage print + 245, // DoSpecial popTop + 71, // PushLiteral 7 + 49, // PushTemporary 1 + 129, // MarkArguments 1 + 152, // SendMessage size + 130, // MarkArguments 2 + 153 // SendMessage - + }; + memcpy(m_bytecode->getBytes(), bytes, sizeof(bytes)/sizeof(bytes[0])); + } + virtual void TearDown() { + free(m_bytecode); + } + TByteObject* m_bytecode; +}; + +void encodeInstruction(const st::TSmalltalkInstruction& inst, uint8_t* bytes, uint16_t& bytePointer) +{ + if (inst.getArgument() <= 0x0F) { + bytes[bytePointer++] = (inst.getOpcode() << 4) | inst.getArgument(); + } else { + bytes[bytePointer++] = inst.getOpcode(); + bytes[bytePointer++] = inst.getArgument(); + } + switch(inst.getOpcode()) + { + case opcode::doPrimitive: + bytes[bytePointer++] = inst.getExtra(); + return; + case opcode::pushBlock: + bytes[bytePointer++] = inst.getExtra(); + bytes[bytePointer++] = inst.getExtra() >> 8; + return; + case opcode::doSpecial: + switch (inst.getArgument()) { + case special::branch: + case special::branchIfTrue: + case special::branchIfFalse: + bytes[bytePointer++] = inst.getExtra(); + bytes[bytePointer++] = inst.getExtra() >> 8; + return; + + case special::sendToSuper: + bytes[bytePointer++] = inst.getExtra(); + return; + } + default: return; + } +} + +class InstructionDecoderSyntheticOpcodesTest : public ::testing::Test +{ +protected: + virtual void SetUp() { + const st::TSmalltalkInstruction::TArgument minArgument = std::numeric_limits::min(); + const st::TSmalltalkInstruction::TArgument maxArgument = std::numeric_limits::max(); + + const st::TSmalltalkInstruction::TExtra minExtra = std::numeric_limits::min(); + const st::TSmalltalkInstruction::TExtra maxExtra = std::numeric_limits::max(); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushInstance, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushInstance, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushArgument, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushArgument, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushTemporary, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushTemporary, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushLiteral, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushLiteral, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushConstant, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushConstant, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::assignInstance, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::assignInstance, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::assignTemporary, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::assignTemporary, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::markArguments, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::markArguments, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::sendMessage, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::sendMessage, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::sendUnary, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::sendUnary, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::sendBinary, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::sendBinary, maxArgument).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushBlock, minArgument, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushBlock, minArgument, maxExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushBlock, maxArgument, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::pushBlock, maxArgument, maxExtra).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doPrimitive, minArgument, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doPrimitive, minArgument, std::numeric_limits::max()).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doPrimitive, maxArgument, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doPrimitive, maxArgument, std::numeric_limits::max()).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::branch, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::branch, maxExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse, maxExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue, maxExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::sendToSuper, minExtra).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, special::sendToSuper, std::numeric_limits::max()).serialize() ); + + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, minArgument).serialize() ); + m_instructions.push_back( st::TSmalltalkInstruction(opcode::doSpecial, maxArgument).serialize() ); + + void* mem = calloc(4, 1024); // magic numbers + m_bytecode = new (mem) TByteObject(420 /*another magic number*/, static_cast(0)); + uint16_t bytePointer = 0; + uint8_t* bytes = m_bytecode->getBytes(); + + for(std::size_t i = 0; i < m_instructions.size(); i++) { + st::TSmalltalkInstruction x( m_instructions[i] ); + encodeInstruction(x, bytes, bytePointer); + } + } + virtual void TearDown() { + free(m_bytecode); + } + std::vector m_instructions; + TByteObject* m_bytecode; +}; + +TEST_F(InstructionDecoderRealMethodTest, bytePointerIsShifted) +{ + uint16_t bytePointerLastValue = 0; + uint16_t bytePointer = bytePointerLastValue; + + while( bytePointer < m_bytecode->getSize() ) { + SCOPED_TRACE(bytePointer); + st::InstructionDecoder::decodeAndShiftPointer(*m_bytecode, bytePointer); + ASSERT_NE(bytePointer, bytePointerLastValue); + bytePointerLastValue = bytePointer; + } +} + +TEST_F(InstructionDecoderSyntheticOpcodesTest, decodeAndShiftPointer) +{ + uint16_t bytePointer = 0; + for(std::size_t i = 0; i < m_instructions.size(); i++) { + SCOPED_TRACE(i); + st::TSmalltalkInstruction x = st::InstructionDecoder::decodeAndShiftPointer(*m_bytecode, bytePointer); + st::TSmalltalkInstruction y = st::TSmalltalkInstruction( m_instructions[i] ); + EXPECT_EQ(x.getOpcode(), y.getOpcode()); + EXPECT_EQ(x.getArgument(), y.getArgument()); + EXPECT_EQ(x.getExtra(), y.getExtra()); + } +} From 160c972ea5721910f00cb52f5f4b057dd46177a6 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 14 May 2014 21:31:15 +0400 Subject: [PATCH 180/290] Adds tests for ControlGraph Issue: #32 --- tests/CMakeLists.txt | 1 + tests/control_graph.cpp | 174 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 tests/control_graph.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9387904..6844e13 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,3 +20,4 @@ include_directories(${CMAKE_SOURCE_DIR}/include) cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;-pthread") cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;-pthread") +cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;-pthread") diff --git a/tests/control_graph.cpp b/tests/control_graph.cpp new file mode 100644 index 0000000..b244a3b --- /dev/null +++ b/tests/control_graph.cpp @@ -0,0 +1,174 @@ +#include +#include +#include + +class ControlGraphTest : public ::testing::Test +{ +protected: + TMethod* m_method; + st::ParsedMethod* m_parsedMethod; + st::ControlGraph* m_cfg; + virtual void SetUp() + { + m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); + static const uint8_t bytes[] = + { + // This is the bytecode for method Object>>isKindOf: + 32, + 129, + 144, + 112, + 245, + 193, + 11, + 0, + 48, + 161, + 242, + 48, + 161, + 248, + 38, + 0, + 48, + 33, + 130, + 145, + 248, + 28, + 0, + 91, + 242, + 246, + 29, + 0, + 90, + 245, + 48, + 129, + 146, + 112, + 245, + 246, + 11, + 0, + 80, + 245, + 92, + 242, + 245, + 241 + }; + TByteObject* bytecode = new ( calloc(4, 4096) ) TByteObject(sizeof(bytes)/sizeof(bytes[0]), static_cast(0)); + memcpy(bytecode->getBytes(), bytes, bytecode->getSize()); + m_method->byteCodes = bytecode; + + m_parsedMethod = new st::ParsedMethod(m_method); + m_cfg = new st::ControlGraph(m_parsedMethod); + m_cfg->buildGraph(); + } + virtual void TearDown() { + free(m_method->byteCodes); + free(m_method); + delete m_cfg; + delete m_parsedMethod; + } +}; + +TEST_F(ControlGraphTest, buildGraphMoreThanOnce) +{ + st::ControlGraph cfg(m_parsedMethod); + cfg.buildGraph(); + //cfg.buildGraph(); + EXPECT_EQ( std::distance(m_cfg->begin(), m_cfg->end()) , std::distance(cfg.begin(), cfg.end()) ); +} + +TEST_F(ControlGraphTest, lastInstIsTerminator) +{ + class lastIsTerminator: public st::BasicBlockVisitor + { + public: + lastIsTerminator(st::ParsedBytecode* parsedBytecode) : st::BasicBlockVisitor(parsedBytecode) {} + virtual bool visitBlock(st::BasicBlock& BB) { + std::size_t bbSize = BB.size(); + EXPECT_NE(bbSize, 0); + st::TSmalltalkInstruction terminator(0); + bool hasTerminator = BB.getTerminator(terminator); + + { + SCOPED_TRACE("Each BB must have a terminator"); + EXPECT_TRUE(hasTerminator); + } + { + SCOPED_TRACE("The instruction returned by BB::getTerminator must be a terminator"); + EXPECT_TRUE( terminator.isTerminator() ); + } + { + SCOPED_TRACE("The last instruction must be a terminator and it must be equal to BB::getTerminator"); + st::TSmalltalkInstruction lastInst = BB[bbSize-1]; + EXPECT_TRUE( lastInst.isTerminator() ); + EXPECT_EQ( lastInst.serialize(), terminator.serialize() ); + } + + return true; + } + }; + lastIsTerminator visitor(m_cfg->getParsedMethod()); + visitor.run(); +} + +TEST_F(ControlGraphTest, eachDomainHasTerminator) +{ + class domainHasTerminator: public st::DomainVisitor + { + public: + TMethod* m_method; + domainHasTerminator(st::ControlGraph* graph, TMethod* method) : st::DomainVisitor(graph), m_method(method) {} + virtual bool visitDomain(st::ControlDomain& domain) { + st::InstructionNode* terminator = domain.getTerminator(); + { + SCOPED_TRACE("Domain must have a terminator"); + EXPECT_NE( terminator, static_cast(0) ); + EXPECT_TRUE( terminator->getInstruction().isTerminator() ); + } + return true; + } + }; + domainHasTerminator visitor(m_cfg, m_method); + visitor.run(); +} + +TEST_F(ControlGraphTest, BBsAreLinkedTogether) +{ + class areBBsLinked: public st::DomainVisitor + { + public: + int& m_numberOfReferrers; + areBBsLinked(st::ControlGraph* graph, int& numberOfReferrers) : + st::DomainVisitor(graph), + m_numberOfReferrers(numberOfReferrers) + {} + virtual bool visitDomain(st::ControlDomain& domain) { + st::BasicBlock::TBasicBlockSet referrers = domain.getBasicBlock()->getReferers(); + m_numberOfReferrers += referrers.size(); + + for(st::BasicBlock::TBasicBlockSet::const_iterator referrer = referrers.begin(); referrer != referrers.end(); ++referrer) { + st::TSmalltalkInstruction terminator(0); + bool referrerHasTerminator = (*referrer)->getTerminator(terminator); + + EXPECT_TRUE(referrerHasTerminator); + EXPECT_TRUE(terminator.isBranch()); + EXPECT_EQ( terminator.getExtra(), domain.getBasicBlock()->getOffset() ); + } + return true; + } + }; + int numberOfReferrers = 0; + areBBsLinked visitor(m_cfg, numberOfReferrers); + visitor.run(); + + { + SCOPED_TRACE("There must be at least one referrer"); + EXPECT_NE(numberOfReferrers, 0); + } +} From 90b26e45fdfe127c167af7f1c159fc1712a9720c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 12 Jun 2014 21:17:57 +0400 Subject: [PATCH 181/290] Adds tests for ABAB (#2) Issue: #32 --- tests/CMakeLists.txt | 1 + tests/abab_problem.cpp | 112 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tests/abab_problem.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6844e13..9a581d9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,3 +21,4 @@ include_directories(${CMAKE_SOURCE_DIR}/include) cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;-pthread") cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;-pthread") cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;-pthread") +cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;-pthread") diff --git a/tests/abab_problem.cpp b/tests/abab_problem.cpp new file mode 100644 index 0000000..5f87284 --- /dev/null +++ b/tests/abab_problem.cpp @@ -0,0 +1,112 @@ +#include +#include +#include + +class ABAB : public ::testing::Test +{ +protected: + TMethod* m_method; + st::ParsedMethod* m_parsedMethod; + st::ControlGraph* m_cfg; + virtual void SetUp() + { + m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); + static const uint8_t bytes[] = + { + 33, // 0000 PushArgument 1 + 248, // 0001 DoSpecial branchIfFalse 8 + 8, + 0, + 81, // 0004 PushConstant 1 + 246, // 0005 DoSpecial branch 9 + 9, + 0, + 83, // 0008 PushConstant 3 + 34, // 0009 PushArgument 2 + 248, // 0010 DoSpecial branchIfFalse 17 + 17, + 0, + 85, // 0013 PushConstant 5 + 246, // 0014 DoSpecial branch 18 + 18, + 0, + 87, // 0017 PushConstant 7 + 178 // 0018 SendBinary + + }; + TByteObject* bytecode = new ( calloc(4, 4096) ) TByteObject(sizeof(bytes)/sizeof(bytes[0]), static_cast(0)); + memcpy(bytecode->getBytes(), bytes, bytecode->getSize()); + m_method->byteCodes = bytecode; + + m_parsedMethod = new st::ParsedMethod(m_method); + m_cfg = new st::ControlGraph(m_parsedMethod); + m_cfg->buildGraph(); + } + virtual void TearDown() { + free(m_method->byteCodes); + free(m_method); + delete m_cfg; + delete m_parsedMethod; + } +}; + +void checkSendBinaryArg(st::ControlNode* arg) +{ + { + SCOPED_TRACE("Each argument of sendBinary is a phi node"); + EXPECT_EQ( arg->getNodeType(), st::ControlNode::ntPhi); + } + { + SCOPED_TRACE("Each argument of sendBinary has 2 egdes"); + EXPECT_EQ( arg->getInEdges().size(), 2); + } + { + SCOPED_TRACE("Each edge of arg-phi is a PushConstant"); + for(st::TNodeSet::iterator i = arg->getInEdges().begin(); i != arg->getInEdges().end(); i++) { + st::ControlNode* edge = *i; + EXPECT_EQ( edge->getNodeType(), st::ControlNode::ntInstruction ); + if (st::InstructionNode* edgeInst = edge->cast()) { + EXPECT_EQ( edgeInst->getInstruction().getOpcode(), opcode::pushConstant ); + } + } + } +} + +TEST_F(ABAB, main) +{ + class ABABProblem: public st::NodeVisitor + { + public: + bool sendBinaryFound; + ABABProblem(st::ControlGraph* graph) : st::NodeVisitor(graph), sendBinaryFound(false) {} + virtual bool visitNode(st::ControlNode& node) { + if ( st::InstructionNode* inst = node.cast() ) + { + if (inst->getInstruction().getOpcode() == opcode::sendBinary) + { + sendBinaryFound = true; + + EXPECT_EQ( inst->getInEdges().size(), 4); // 2 branches + 2 phis + EXPECT_EQ( inst->getArgumentsCount(), 2); + st::ControlNode* firstArg = inst->getArgument(0); + st::ControlNode* secondArg = inst->getArgument(1); + EXPECT_NE(firstArg, secondArg); + + { + SCOPED_TRACE("Check first arg"); + checkSendBinaryArg(firstArg); + } + { + SCOPED_TRACE("Check second arg"); + checkSendBinaryArg(secondArg); + } + + return false; + } + } + return true; + } + }; + ABABProblem abab(m_cfg); + abab.run(); + EXPECT_TRUE(abab.sendBinaryFound); +} From f22432f917c11a749f45a264b5ec86636cb27a7d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 14 Jun 2014 13:49:11 +0400 Subject: [PATCH 182/290] Adds tests for stack semantics Issue: #32 --- tests/CMakeLists.txt | 1 + tests/stack_semantics.cpp | 86 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/stack_semantics.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9a581d9..33c4ad5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,3 +22,4 @@ cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOUR cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;-pthread") cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;-pthread") cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;-pthread") +cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;-pthread") diff --git a/tests/stack_semantics.cpp b/tests/stack_semantics.cpp new file mode 100644 index 0000000..7e55b1a --- /dev/null +++ b/tests/stack_semantics.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +class StackSemanticsTemps : public ::testing::Test +{ +protected: + TMethod* m_method; + st::ParsedMethod* m_parsedMethod; + st::ControlGraph* m_cfg; + virtual void SetUp() + { + m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); + static const uint8_t bytes[] = + { + //81 112 245 48 33 248 15 0 83 112 245 48 246 16 0 85 178 245 241 + 81, // 0000 PushConstant 1 + 112, // 0001 AssignTemporary 0 + 245, // 0002 DoSpecial popTop + 48, // 0003 PushTemporary 0 + 33, // 0004 PushArgument 1 + 248, // 0005 DoSpecial branchIfFalse 15 + 15, // + 0, // + 83, // 0008 PushConstant 3 + 112, // 0009 AssignTemporary 0 + 245, // 0010 DoSpecial popTop + 48, // 0011 PushTemporary 0 + 246, // 0012 DoSpecial branch 16 + 16, // + 0, // + 85, // 0015 PushConstant 5 + 178 // 0016 SendBinary + + }; + TByteObject* bytecode = new ( calloc(4, 4096) ) TByteObject(sizeof(bytes)/sizeof(bytes[0]), static_cast(0)); + memcpy(bytecode->getBytes(), bytes, bytecode->getSize()); + m_method->byteCodes = bytecode; + + m_parsedMethod = new st::ParsedMethod(m_method); + m_cfg = new st::ControlGraph(m_parsedMethod); + m_cfg->buildGraph(); + } + virtual void TearDown() { + free(m_method->byteCodes); + free(m_method); + delete m_cfg; + delete m_parsedMethod; + } +}; + +TEST_F(StackSemanticsTemps, loadInCorrectBB) +{ + class TempsLoadInCorrectBB: public st::NodeVisitor + { + public: + TempsLoadInCorrectBB(st::ControlGraph* graph) : st::NodeVisitor(graph) {} + virtual bool visitNode(st::ControlNode& node) { + if ( st::InstructionNode* inst = node.cast() ) + { + if (inst->getInstruction().getOpcode() == opcode::sendBinary) + { + st::ControlNode* firstArg = inst->getArgument(0); + st::ControlNode* secondArg = inst->getArgument(1); + EXPECT_NE(firstArg, secondArg); + + { + SCOPED_TRACE("Second arg must be phi"); + EXPECT_EQ( secondArg->getNodeType(), st::ControlNode::ntPhi ); + } + { + SCOPED_TRACE("First arg must be PushTemporary"); + EXPECT_EQ( secondArg->getNodeType(), st::ControlNode::ntInstruction ); + st::InstructionNode* pushTemp = secondArg->cast(); + EXPECT_EQ(pushTemp->getInstruction().getOpcode(), opcode::pushTemporary); + } + + return false; + } + } + return true; + } + }; + TempsLoadInCorrectBB test(m_cfg); + test.run(); +} From e7cddb65d352a0f7e9f02f75b6cf3763e7597f3d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 4 Jul 2014 20:42:42 +0400 Subject: [PATCH 183/290] Adds test pattern for decoding bytecode Issue: #32 --- tests/abab_problem.cpp | 77 +++++++----------- tests/control_graph.cpp | 134 ++++++++++++++------------------ tests/patterns/DecodeBytecode.h | 46 +++++++++++ tests/stack_semantics.cpp | 74 +++++++----------- 4 files changed, 157 insertions(+), 174 deletions(-) create mode 100644 tests/patterns/DecodeBytecode.h diff --git a/tests/abab_problem.cpp b/tests/abab_problem.cpp index 5f87284..c2eebe3 100644 --- a/tests/abab_problem.cpp +++ b/tests/abab_problem.cpp @@ -1,54 +1,33 @@ -#include -#include -#include +#include "patterns/DecodeBytecode.h" -class ABAB : public ::testing::Test -{ -protected: - TMethod* m_method; - st::ParsedMethod* m_parsedMethod; - st::ControlGraph* m_cfg; - virtual void SetUp() - { - m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); - static const uint8_t bytes[] = - { - 33, // 0000 PushArgument 1 - 248, // 0001 DoSpecial branchIfFalse 8 - 8, - 0, - 81, // 0004 PushConstant 1 - 246, // 0005 DoSpecial branch 9 - 9, - 0, - 83, // 0008 PushConstant 3 - 34, // 0009 PushArgument 2 - 248, // 0010 DoSpecial branchIfFalse 17 - 17, - 0, - 85, // 0013 PushConstant 5 - 246, // 0014 DoSpecial branch 18 - 18, - 0, - 87, // 0017 PushConstant 7 - 178 // 0018 SendBinary + - }; - TByteObject* bytecode = new ( calloc(4, 4096) ) TByteObject(sizeof(bytes)/sizeof(bytes[0]), static_cast(0)); - memcpy(bytecode->getBytes(), bytes, bytecode->getSize()); - m_method->byteCodes = bytecode; - - m_parsedMethod = new st::ParsedMethod(m_method); - m_cfg = new st::ControlGraph(m_parsedMethod); - m_cfg->buildGraph(); - } - virtual void TearDown() { - free(m_method->byteCodes); - free(m_method); - delete m_cfg; - delete m_parsedMethod; - } +static const uint8_t ABABbytecode[] = { + 33, // 0000 PushArgument 1 + 248, // 0001 DoSpecial branchIfFalse 8 + 8, + 0, + 81, // 0004 PushConstant 1 + 246, // 0005 DoSpecial branch 9 + 9, + 0, + 83, // 0008 PushConstant 3 + 34, // 0009 PushArgument 2 + 248, // 0010 DoSpecial branchIfFalse 17 + 17, + 0, + 85, // 0013 PushConstant 5 + 246, // 0014 DoSpecial branch 18 + 18, + 0, + 87, // 0017 PushConstant 7 + 178 // 0018 SendBinary + }; +INSTANTIATE_TEST_CASE_P( + testABAB, + P_DecodeBytecode, + ::testing::Values( std::tr1::make_tuple(std::string("Bytecode for ABAB"), std::string(reinterpret_cast(ABABbytecode), sizeof(ABABbytecode))) ) +); + void checkSendBinaryArg(st::ControlNode* arg) { { @@ -71,7 +50,7 @@ void checkSendBinaryArg(st::ControlNode* arg) } } -TEST_F(ABAB, main) +TEST_P(P_DecodeBytecode, ABAB) { class ABABProblem: public st::NodeVisitor { diff --git a/tests/control_graph.cpp b/tests/control_graph.cpp index b244a3b..3e6715b 100644 --- a/tests/control_graph.cpp +++ b/tests/control_graph.cpp @@ -1,81 +1,61 @@ -#include -#include -#include +#include "patterns/DecodeBytecode.h" -class ControlGraphTest : public ::testing::Test +static const uint8_t bytecode[] = { -protected: - TMethod* m_method; - st::ParsedMethod* m_parsedMethod; - st::ControlGraph* m_cfg; - virtual void SetUp() - { - m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); - static const uint8_t bytes[] = - { - // This is the bytecode for method Object>>isKindOf: - 32, - 129, - 144, - 112, - 245, - 193, - 11, - 0, - 48, - 161, - 242, - 48, - 161, - 248, - 38, - 0, - 48, - 33, - 130, - 145, - 248, - 28, - 0, - 91, - 242, - 246, - 29, - 0, - 90, - 245, - 48, - 129, - 146, - 112, - 245, - 246, - 11, - 0, - 80, - 245, - 92, - 242, - 245, - 241 - }; - TByteObject* bytecode = new ( calloc(4, 4096) ) TByteObject(sizeof(bytes)/sizeof(bytes[0]), static_cast(0)); - memcpy(bytecode->getBytes(), bytes, bytecode->getSize()); - m_method->byteCodes = bytecode; - - m_parsedMethod = new st::ParsedMethod(m_method); - m_cfg = new st::ControlGraph(m_parsedMethod); - m_cfg->buildGraph(); - } - virtual void TearDown() { - free(m_method->byteCodes); - free(m_method); - delete m_cfg; - delete m_parsedMethod; - } + // This is the bytecode for method Object>>isKindOf: + 32, + 129, + 144, + 112, + 245, + 193, + 11, + 0, + 48, + 161, + 242, + 48, + 161, + 248, + 38, + 0, + 48, + 33, + 130, + 145, + 248, + 28, + 0, + 91, + 242, + 246, + 29, + 0, + 90, + 245, + 48, + 129, + 146, + 112, + 245, + 246, + 11, + 0, + 80, + 245, + 92, + 242, + 245, + 241 }; -TEST_F(ControlGraphTest, buildGraphMoreThanOnce) +INSTANTIATE_TEST_CASE_P( + testCFG, + P_DecodeBytecode, + ::testing::Values( std::tr1::make_tuple(std::string("Object>>isKindOf:"), std::string(reinterpret_cast(bytecode), sizeof(bytecode))) ) +); + +TEST_P(P_DecodeBytecode, buildGraphMoreThanOnce) { st::ControlGraph cfg(m_parsedMethod); cfg.buildGraph(); @@ -83,7 +63,7 @@ TEST_F(ControlGraphTest, buildGraphMoreThanOnce) EXPECT_EQ( std::distance(m_cfg->begin(), m_cfg->end()) , std::distance(cfg.begin(), cfg.end()) ); } -TEST_F(ControlGraphTest, lastInstIsTerminator) +TEST_P(P_DecodeBytecode, lastInstIsTerminator) { class lastIsTerminator: public st::BasicBlockVisitor { @@ -117,7 +97,7 @@ TEST_F(ControlGraphTest, lastInstIsTerminator) visitor.run(); } -TEST_F(ControlGraphTest, eachDomainHasTerminator) +TEST_P(P_DecodeBytecode, eachDomainHasTerminator) { class domainHasTerminator: public st::DomainVisitor { @@ -138,7 +118,7 @@ TEST_F(ControlGraphTest, eachDomainHasTerminator) visitor.run(); } -TEST_F(ControlGraphTest, BBsAreLinkedTogether) +TEST_P(P_DecodeBytecode, BBsAreLinkedTogether) { class areBBsLinked: public st::DomainVisitor { diff --git a/tests/patterns/DecodeBytecode.h b/tests/patterns/DecodeBytecode.h new file mode 100644 index 0000000..7b6394d --- /dev/null +++ b/tests/patterns/DecodeBytecode.h @@ -0,0 +1,46 @@ +#ifndef LLST_PATTERN_DECODE_BYTECODE_INCLUDED +#define LLST_PATTERN_DECODE_BYTECODE_INCLUDED + +#include +#include +#include + +void __assert_fail(const char *__assertion, const char *__file, unsigned int __line, const char *__function) +{ + std::stringstream ss; + ss << "Assertion '" << __assertion << "' failed in '" << __file << "' at: " << __line; + throw ss.str(); +} + +class P_DecodeBytecode : public ::testing::TestWithParam > +{ +public: + virtual ~P_DecodeBytecode() {} + TMethod* m_method; + st::ParsedMethod* m_parsedMethod; + st::ControlGraph* m_cfg; + virtual void SetUp() + { + std::string bytecode = std::tr1::get<1>(GetParam()); + m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); + TByteObject* byteCodes = new ( calloc(4, 4096) ) TByteObject(bytecode.length(), static_cast(0)); + memcpy(byteCodes->getBytes(), bytecode.c_str() , byteCodes->getSize()); + m_method->byteCodes = byteCodes; + + m_parsedMethod = new st::ParsedMethod(m_method); + m_cfg = new st::ControlGraph(m_parsedMethod); + try { + m_cfg->buildGraph(); + } catch (std::string& e) { + FAIL() << e; + } + } + virtual void TearDown() { + free(m_method->byteCodes); + free(m_method); + delete m_cfg; + delete m_parsedMethod; + } +}; + +#endif diff --git a/tests/stack_semantics.cpp b/tests/stack_semantics.cpp index 7e55b1a..b24b5a0 100644 --- a/tests/stack_semantics.cpp +++ b/tests/stack_semantics.cpp @@ -1,55 +1,33 @@ -#include -#include -#include -#include +#include "patterns/DecodeBytecode.h" -class StackSemanticsTemps : public ::testing::Test +static const uint8_t bytecode[] = { -protected: - TMethod* m_method; - st::ParsedMethod* m_parsedMethod; - st::ControlGraph* m_cfg; - virtual void SetUp() - { - m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); - static const uint8_t bytes[] = - { - //81 112 245 48 33 248 15 0 83 112 245 48 246 16 0 85 178 245 241 - 81, // 0000 PushConstant 1 - 112, // 0001 AssignTemporary 0 - 245, // 0002 DoSpecial popTop - 48, // 0003 PushTemporary 0 - 33, // 0004 PushArgument 1 - 248, // 0005 DoSpecial branchIfFalse 15 - 15, // - 0, // - 83, // 0008 PushConstant 3 - 112, // 0009 AssignTemporary 0 - 245, // 0010 DoSpecial popTop - 48, // 0011 PushTemporary 0 - 246, // 0012 DoSpecial branch 16 - 16, // - 0, // - 85, // 0015 PushConstant 5 - 178 // 0016 SendBinary + - }; - TByteObject* bytecode = new ( calloc(4, 4096) ) TByteObject(sizeof(bytes)/sizeof(bytes[0]), static_cast(0)); - memcpy(bytecode->getBytes(), bytes, bytecode->getSize()); - m_method->byteCodes = bytecode; - - m_parsedMethod = new st::ParsedMethod(m_method); - m_cfg = new st::ControlGraph(m_parsedMethod); - m_cfg->buildGraph(); - } - virtual void TearDown() { - free(m_method->byteCodes); - free(m_method); - delete m_cfg; - delete m_parsedMethod; - } + 81, // 0000 PushConstant 1 + 112, // 0001 AssignTemporary 0 + 245, // 0002 DoSpecial popTop + 48, // 0003 PushTemporary 0 + 33, // 0004 PushArgument 1 + 248, // 0005 DoSpecial branchIfFalse 15 + 15, // + 0, // + 83, // 0008 PushConstant 3 + 112, // 0009 AssignTemporary 0 + 245, // 0010 DoSpecial popTop + 48, // 0011 PushTemporary 0 + 246, // 0012 DoSpecial branch 16 + 16, // + 0, // + 85, // 0015 PushConstant 5 + 178 // 0016 SendBinary + }; -TEST_F(StackSemanticsTemps, loadInCorrectBB) +INSTANTIATE_TEST_CASE_P( + testSemantics, + P_DecodeBytecode, + ::testing::Values( std::tr1::make_tuple(std::string("Bytecode for temps"), std::string(reinterpret_cast(bytecode), sizeof(bytecode))) ) +); + +TEST_F(P_DecodeBytecode, StackSemanticsTemps) { class TempsLoadInCorrectBB: public st::NodeVisitor { From 1a3153edd1ec5c49b31946d3e0c5386d70552ad0 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 31 Jan 2015 23:29:30 +0300 Subject: [PATCH 184/290] Makes tests run after rebase etc Issue: #32 --- tests/CMakeLists.txt | 2 +- tests/abab_problem.cpp | 4 +--- tests/control_graph.cpp | 4 +--- tests/patterns/DecodeBytecode.h | 10 +++++++++- tests/stack_semantics.cpp | 11 ++++------- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 33c4ad5..66ae256 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,8 +10,8 @@ macro(cxx_test pretty_name bin_name sources libs) set_target_properties(${bin_name} PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") endif() + target_link_libraries(${bin_name} ${libs} ${GTEST_BOTH_LIBRARIES}) set_target_properties(${bin_name} PROPERTIES COMPILE_DEFINITIONS TESTS_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\") - target_link_libraries(${bin_name} ${libs} ${GTEST_BOTH_LIBRARIES} ) add_test(${pretty_name} ${bin_name}) add_dependencies(check ${bin_name}) endmacro() diff --git a/tests/abab_problem.cpp b/tests/abab_problem.cpp index c2eebe3..4db393e 100644 --- a/tests/abab_problem.cpp +++ b/tests/abab_problem.cpp @@ -22,9 +22,7 @@ static const uint8_t ABABbytecode[] = { 178 // 0018 SendBinary + }; -INSTANTIATE_TEST_CASE_P( - testABAB, - P_DecodeBytecode, +INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, ::testing::Values( std::tr1::make_tuple(std::string("Bytecode for ABAB"), std::string(reinterpret_cast(ABABbytecode), sizeof(ABABbytecode))) ) ); diff --git a/tests/control_graph.cpp b/tests/control_graph.cpp index 3e6715b..75e4c4d 100644 --- a/tests/control_graph.cpp +++ b/tests/control_graph.cpp @@ -49,9 +49,7 @@ static const uint8_t bytecode[] = 241 }; -INSTANTIATE_TEST_CASE_P( - testCFG, - P_DecodeBytecode, +INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, ::testing::Values( std::tr1::make_tuple(std::string("Object>>isKindOf:"), std::string(reinterpret_cast(bytecode), sizeof(bytecode))) ) ); diff --git a/tests/patterns/DecodeBytecode.h b/tests/patterns/DecodeBytecode.h index 7b6394d..7c4a607 100644 --- a/tests/patterns/DecodeBytecode.h +++ b/tests/patterns/DecodeBytecode.h @@ -17,11 +17,19 @@ class P_DecodeBytecode : public ::testing::TestWithParam(GetParam()); + bool is_parameterized_test = ::testing::UnitTest::GetInstance()->current_test_info()->value_param(); + if (!is_parameterized_test) { + // Use TEST_P instead of TEST_F ! + abort(); + } + ParamType params = GetParam(); + m_method_name = std::tr1::get<0>(params); + std::string bytecode = std::tr1::get<1>(params); m_method = (new ( calloc(4, sizeof(TMethod)) ) TObject(sizeof(TMethod) / sizeof(TObject*) - 2, 0))->cast(); TByteObject* byteCodes = new ( calloc(4, 4096) ) TByteObject(bytecode.length(), static_cast(0)); memcpy(byteCodes->getBytes(), bytecode.c_str() , byteCodes->getSize()); diff --git a/tests/stack_semantics.cpp b/tests/stack_semantics.cpp index b24b5a0..e35dc93 100644 --- a/tests/stack_semantics.cpp +++ b/tests/stack_semantics.cpp @@ -1,7 +1,6 @@ #include "patterns/DecodeBytecode.h" -static const uint8_t bytecode[] = -{ +static const uint8_t bytecode[] = { 81, // 0000 PushConstant 1 112, // 0001 AssignTemporary 0 245, // 0002 DoSpecial popTop @@ -21,13 +20,11 @@ static const uint8_t bytecode[] = 178 // 0016 SendBinary + }; -INSTANTIATE_TEST_CASE_P( - testSemantics, - P_DecodeBytecode, - ::testing::Values( std::tr1::make_tuple(std::string("Bytecode for temps"), std::string(reinterpret_cast(bytecode), sizeof(bytecode))) ) +INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, + ::testing::Values( std::tr1::make_tuple(std::string("Bytecode"), std::string(reinterpret_cast(bytecode), sizeof(bytecode))) ) ); -TEST_F(P_DecodeBytecode, StackSemanticsTemps) +TEST_P(P_DecodeBytecode, StackSemanticsTemps) { class TempsLoadInCorrectBB: public st::NodeVisitor { From 03826cefd337b935cd09f0880294d04e5798f8ca Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Wed, 4 Feb 2015 00:04:54 +0600 Subject: [PATCH 185/290] Fixes stack_semantics test --- tests/stack_semantics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/stack_semantics.cpp b/tests/stack_semantics.cpp index e35dc93..f626e6e 100644 --- a/tests/stack_semantics.cpp +++ b/tests/stack_semantics.cpp @@ -45,8 +45,9 @@ TEST_P(P_DecodeBytecode, StackSemanticsTemps) } { SCOPED_TRACE("First arg must be PushTemporary"); - EXPECT_EQ( secondArg->getNodeType(), st::ControlNode::ntInstruction ); - st::InstructionNode* pushTemp = secondArg->cast(); + EXPECT_EQ( firstArg->getNodeType(), st::ControlNode::ntInstruction ); + st::InstructionNode* pushTemp = firstArg->cast(); + EXPECT_TRUE(pushTemp); EXPECT_EQ(pushTemp->getInstruction().getOpcode(), opcode::pushTemporary); } From 7cf4e009c91426a0a30bae09e08791e5f012dcb5 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 00:30:48 +0300 Subject: [PATCH 186/290] Add test for DecodeAllMethods Issue: #32 --- .gitattributes | 1 + tests/CMakeLists.txt | 1 + tests/data/DecodeAllMethods.image | Bin 0 -> 209842 bytes tests/decode_all_methods.cpp | 47 ++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 tests/data/DecodeAllMethods.image create mode 100644 tests/decode_all_methods.cpp diff --git a/.gitattributes b/.gitattributes index 7176cb1..4d26d05 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *man -diff ChangeLog -diff +tests/data/ -diff diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 66ae256..d60d3b0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,3 +23,4 @@ cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;-pthread") cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;-pthread") cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;-pthread") +cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set;-pthread") diff --git a/tests/data/DecodeAllMethods.image b/tests/data/DecodeAllMethods.image new file mode 100644 index 0000000000000000000000000000000000000000..149a438cd92334a43d3e44ec6a58dd3dc357a287 GIT binary patch literal 209842 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 Hz4reCGbnOS literal 0 HcmV?d00001 diff --git a/tests/decode_all_methods.cpp b/tests/decode_all_methods.cpp new file mode 100644 index 0000000..159795d --- /dev/null +++ b/tests/decode_all_methods.cpp @@ -0,0 +1,47 @@ +#include "patterns/DecodeBytecode.h" +#include + +#include + +TEST_P(P_DecodeBytecode, DecodeMethod) +{ + // Do nothing, because the method has already been decoded +} + +typedef std::vector< std::tr1::tuple > MethodsT; +MethodsT getMethods(); + +INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, ::testing::ValuesIn(getMethods()) ); + +MethodsT getMethods() +{ + std::auto_ptr memoryManager(new BakerMemoryManager()); + memoryManager->initializeHeap(1024*1024, 1024*1024); + std::auto_ptr smalltalkImage(new Image(memoryManager.get())); + smalltalkImage->loadImage(TESTS_DIR "./data/DecodeAllMethods.image"); + + MethodsT imageMethods; + TDictionary* imageGlobals = globals.globalsObject; + for (uint32_t i = 0; i < imageGlobals->keys->getSize(); i++) + { + TSymbol* key = (*imageGlobals->keys)[i]; + TObject* value = (*imageGlobals->values)[i]; + + std::string keyString = key->toString(); + char firstLetter = keyString[0]; + if ( keyString != "Smalltalk" && std::isupper(firstLetter) ) { + TClass* currentClass = static_cast(value); + std::string className = currentClass->name->toString(); + + TSymbolArray* names = currentClass->methods->keys; + TObjectArray* methods = currentClass->methods->values; + for (uint32_t m = 0; m < methods->getSize(); m++) { + std::string methodName = (*names)[m]->toString(); + TMethod* method = static_cast( (*methods)[m] ); + std::string bytecode = std::string(reinterpret_cast(method->byteCodes->getBytes()), method->byteCodes->getSize()); + imageMethods.push_back( std::tr1::make_tuple(className + ">>" + methodName, bytecode) ); + } + } + } + return imageMethods; +} From 6462cca72ae7e36d98fc1c0cbb408ad50e64acbb Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 01:19:15 +0300 Subject: [PATCH 187/290] Moves test cases for ControlGraph in a standalone `helper' class. Adds checks whether the graph is correct into DecodeAllMethods. Issue: #32 --- tests/control_graph.cpp | 79 ++----------------------------- tests/decode_all_methods.cpp | 17 ++++++- tests/helpers/ControlGraph.h | 90 ++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 76 deletions(-) create mode 100644 tests/helpers/ControlGraph.h diff --git a/tests/control_graph.cpp b/tests/control_graph.cpp index 75e4c4d..afe76d2 100644 --- a/tests/control_graph.cpp +++ b/tests/control_graph.cpp @@ -1,4 +1,5 @@ #include "patterns/DecodeBytecode.h" +#include "helpers/ControlGraph.h" static const uint8_t bytecode[] = { @@ -63,90 +64,18 @@ TEST_P(P_DecodeBytecode, buildGraphMoreThanOnce) TEST_P(P_DecodeBytecode, lastInstIsTerminator) { - class lastIsTerminator: public st::BasicBlockVisitor - { - public: - lastIsTerminator(st::ParsedBytecode* parsedBytecode) : st::BasicBlockVisitor(parsedBytecode) {} - virtual bool visitBlock(st::BasicBlock& BB) { - std::size_t bbSize = BB.size(); - EXPECT_NE(bbSize, 0); - st::TSmalltalkInstruction terminator(0); - bool hasTerminator = BB.getTerminator(terminator); - - { - SCOPED_TRACE("Each BB must have a terminator"); - EXPECT_TRUE(hasTerminator); - } - { - SCOPED_TRACE("The instruction returned by BB::getTerminator must be a terminator"); - EXPECT_TRUE( terminator.isTerminator() ); - } - { - SCOPED_TRACE("The last instruction must be a terminator and it must be equal to BB::getTerminator"); - st::TSmalltalkInstruction lastInst = BB[bbSize-1]; - EXPECT_TRUE( lastInst.isTerminator() ); - EXPECT_EQ( lastInst.serialize(), terminator.serialize() ); - } - - return true; - } - }; - lastIsTerminator visitor(m_cfg->getParsedMethod()); + H_LastInstIsTerminator visitor(m_cfg->getParsedMethod()); visitor.run(); } TEST_P(P_DecodeBytecode, eachDomainHasTerminator) { - class domainHasTerminator: public st::DomainVisitor - { - public: - TMethod* m_method; - domainHasTerminator(st::ControlGraph* graph, TMethod* method) : st::DomainVisitor(graph), m_method(method) {} - virtual bool visitDomain(st::ControlDomain& domain) { - st::InstructionNode* terminator = domain.getTerminator(); - { - SCOPED_TRACE("Domain must have a terminator"); - EXPECT_NE( terminator, static_cast(0) ); - EXPECT_TRUE( terminator->getInstruction().isTerminator() ); - } - return true; - } - }; - domainHasTerminator visitor(m_cfg, m_method); + H_DomainHasTerminator visitor(m_cfg); visitor.run(); } TEST_P(P_DecodeBytecode, BBsAreLinkedTogether) { - class areBBsLinked: public st::DomainVisitor - { - public: - int& m_numberOfReferrers; - areBBsLinked(st::ControlGraph* graph, int& numberOfReferrers) : - st::DomainVisitor(graph), - m_numberOfReferrers(numberOfReferrers) - {} - virtual bool visitDomain(st::ControlDomain& domain) { - st::BasicBlock::TBasicBlockSet referrers = domain.getBasicBlock()->getReferers(); - m_numberOfReferrers += referrers.size(); - - for(st::BasicBlock::TBasicBlockSet::const_iterator referrer = referrers.begin(); referrer != referrers.end(); ++referrer) { - st::TSmalltalkInstruction terminator(0); - bool referrerHasTerminator = (*referrer)->getTerminator(terminator); - - EXPECT_TRUE(referrerHasTerminator); - EXPECT_TRUE(terminator.isBranch()); - EXPECT_EQ( terminator.getExtra(), domain.getBasicBlock()->getOffset() ); - } - return true; - } - }; - int numberOfReferrers = 0; - areBBsLinked visitor(m_cfg, numberOfReferrers); + H_AreBBsLinked visitor(m_cfg); visitor.run(); - - { - SCOPED_TRACE("There must be at least one referrer"); - EXPECT_NE(numberOfReferrers, 0); - } } diff --git a/tests/decode_all_methods.cpp b/tests/decode_all_methods.cpp index 159795d..52426e8 100644 --- a/tests/decode_all_methods.cpp +++ b/tests/decode_all_methods.cpp @@ -1,11 +1,26 @@ #include "patterns/DecodeBytecode.h" +#include "helpers/ControlGraph.h" #include #include TEST_P(P_DecodeBytecode, DecodeMethod) { - // Do nothing, because the method has already been decoded + /* The method has already been decoded. + * Now we check the method for its correctness. + */ + { + H_LastInstIsTerminator visitor(m_cfg->getParsedMethod()); + visitor.run(); + } + { + H_DomainHasTerminator visitor(m_cfg); + visitor.run(); + } + { + H_AreBBsLinked visitor(m_cfg); + visitor.run(); + } } typedef std::vector< std::tr1::tuple > MethodsT; diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h new file mode 100644 index 0000000..b365ad7 --- /dev/null +++ b/tests/helpers/ControlGraph.h @@ -0,0 +1,90 @@ +#ifndef LLST_HELPER_CONTROL_GRAPH_INCLUDED +#define LLST_HELPER_CONTROL_GRAPH_INCLUDED + +#include +#include +#include + +class H_LastInstIsTerminator: public st::BasicBlockVisitor +{ +public: + H_LastInstIsTerminator(st::ParsedBytecode* parsedBytecode) : st::BasicBlockVisitor(parsedBytecode) {} + virtual bool visitBlock(st::BasicBlock& BB) { + std::size_t bbSize = BB.size(); + if (bbSize == 0) + return true; + + st::TSmalltalkInstruction terminator(0); + bool hasTerminator = BB.getTerminator(terminator); + { + SCOPED_TRACE("Each BB must have a terminator"); + EXPECT_TRUE(hasTerminator); + } + { + SCOPED_TRACE("The instruction returned by BB::getTerminator must be a terminator"); + EXPECT_TRUE( terminator.isTerminator() ); + } + { + SCOPED_TRACE("The last instruction must be a terminator and it must be equal to BB::getTerminator"); + st::TSmalltalkInstruction lastInst = BB[bbSize-1]; + EXPECT_TRUE( lastInst.isTerminator() ); + EXPECT_EQ( lastInst.serialize(), terminator.serialize() ); + } + { + SCOPED_TRACE("There must be no terminators but the last one"); + int terminatorsCount = 0; + st::BasicBlock::iterator iInstruction = BB.begin(); + st::BasicBlock::iterator iEnd = BB.end()-1; + + while (iInstruction != iEnd) { + bool isTerminator = (*iInstruction).isTerminator(); + if (isTerminator) + terminatorsCount++; + EXPECT_FALSE(isTerminator); + ++iInstruction; + } + EXPECT_EQ(0, terminatorsCount); + } + + return true; + } +}; + +class H_DomainHasTerminator: public st::DomainVisitor +{ +public: + H_DomainHasTerminator(st::ControlGraph* graph) : st::DomainVisitor(graph) {} + virtual bool visitDomain(st::ControlDomain& domain) { + st::InstructionNode* terminator = domain.getTerminator(); + { + SCOPED_TRACE("Each domain must have a terminator"); + if (!terminator) + EXPECT_TRUE( terminator != NULL ); + else + EXPECT_TRUE( terminator->getInstruction().isTerminator() ); + } + return true; + } +}; + +class H_AreBBsLinked: public st::DomainVisitor +{ +public: + H_AreBBsLinked(st::ControlGraph* graph) : st::DomainVisitor(graph) {} + virtual bool visitDomain(st::ControlDomain& domain) { + st::BasicBlock::TBasicBlockSet referrers = domain.getBasicBlock()->getReferers(); + + for(st::BasicBlock::TBasicBlockSet::const_iterator referrer = referrers.begin(); referrer != referrers.end(); ++referrer) { + st::TSmalltalkInstruction terminator(0); + bool referrerHasTerminator = (*referrer)->getTerminator(terminator); + + EXPECT_TRUE(referrerHasTerminator); + EXPECT_TRUE(terminator.isBranch()); + EXPECT_EQ( terminator.getExtra(), domain.getBasicBlock()->getOffset() ) + << "The destination of terminator must be the offset of the first instruction of BB"; + } + return true; + } +}; + +#endif From 9d1ff6512ad8205dea45fc77937ce8b20d187132 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 03:04:04 +0300 Subject: [PATCH 188/290] Adds helper to check the number of egdes(and arguments) Issue: #32 --- tests/decode_all_methods.cpp | 13 +----- tests/helpers/ControlGraph.h | 85 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/tests/decode_all_methods.cpp b/tests/decode_all_methods.cpp index 52426e8..23d5d55 100644 --- a/tests/decode_all_methods.cpp +++ b/tests/decode_all_methods.cpp @@ -9,18 +9,7 @@ TEST_P(P_DecodeBytecode, DecodeMethod) /* The method has already been decoded. * Now we check the method for its correctness. */ - { - H_LastInstIsTerminator visitor(m_cfg->getParsedMethod()); - visitor.run(); - } - { - H_DomainHasTerminator visitor(m_cfg); - visitor.run(); - } - { - H_AreBBsLinked visitor(m_cfg); - visitor.run(); - } + H_CheckCFGCorrect(m_cfg); } typedef std::vector< std::tr1::tuple > MethodsT; diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index b365ad7..b07bbe3 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -87,4 +87,89 @@ class H_AreBBsLinked: public st::DomainVisitor } }; +class H_CorrectNumOfEdges: public st::NodeVisitor +{ +public: + H_CorrectNumOfEdges(st::ControlGraph* graph) : st::NodeVisitor(graph) {} + virtual bool visitNode(st::ControlNode& node) { + if (st::InstructionNode* inst = node.cast()) + { + SCOPED_TRACE(inst->getInstruction().toString()); + switch (inst->getInstruction().getOpcode()) { + case opcode::pushInstance: + case opcode::pushArgument: + case opcode::pushTemporary: + case opcode::pushLiteral: + case opcode::pushConstant: + case opcode::pushBlock: + EXPECT_EQ( inst->getArgumentsCount(), 0); + break; + case opcode::sendUnary: + case opcode::assignInstance: + case opcode::assignTemporary: + EXPECT_EQ( inst->getArgumentsCount(), 1); + break; + case opcode::sendBinary: + EXPECT_EQ( inst->getArgumentsCount(), 2); + break; + case opcode::doSpecial: { + switch (inst->getInstruction().getArgument()) { + case special::stackReturn: + case special::blockReturn: + case special::popTop: + case special::branchIfTrue: + case special::branchIfFalse: + case special::duplicate: + EXPECT_EQ( inst->getArgumentsCount(), 1); + break; + case special::branch: + EXPECT_EQ( inst->getArgumentsCount(), 0); + break; + case special::sendToSuper: + EXPECT_EQ( inst->getArgumentsCount(), 1); + break; + } + } break; + default: + EXPECT_GE( inst->getArgumentsCount(), 1); + break; + } + if (inst->getInstruction().isValueProvider()) { + const st::TNodeSet& consumers = inst->getConsumers(); + EXPECT_GT(consumers.size(), 0); + } + } + if (st::PhiNode* phi = node.cast()) { + const st::TNodeSet& inEdges = phi->getInEdges(); + const st::TNodeSet& outEdges = phi->getOutEdges(); + EXPECT_GT(inEdges.size(), 0); + EXPECT_GT(outEdges.size(), 0); + } + if (st::TauNode* tau = node.cast()) { + EXPECT_TRUE(tau == NULL /* always fails */); // TODO + } + return true; + } +}; + +void H_CheckCFGCorrect(st::ControlGraph* graph) +{ + { + H_LastInstIsTerminator visitor(graph->getParsedMethod()); + visitor.run(); + } + { + H_DomainHasTerminator visitor(graph); + visitor.run(); + } + { + H_AreBBsLinked visitor(graph); + visitor.run(); + } + { + H_CorrectNumOfEdges visitor(graph); + visitor.run(); + } +} + #endif From 782eee24c9025fcf126d782abd8dbb0ab0a0f0d1 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 18:19:15 +0300 Subject: [PATCH 189/290] Fixes test case checking whether BBs are linked together Issue: #32 --- tests/helpers/ControlGraph.h | 50 +++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index b07bbe3..3ff39b1 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -67,21 +67,47 @@ class H_DomainHasTerminator: public st::DomainVisitor } }; -class H_AreBBsLinked: public st::DomainVisitor +class H_AreBBsLinked: public st::NodeVisitor { public: - H_AreBBsLinked(st::ControlGraph* graph) : st::DomainVisitor(graph) {} - virtual bool visitDomain(st::ControlDomain& domain) { - st::BasicBlock::TBasicBlockSet referrers = domain.getBasicBlock()->getReferers(); - - for(st::BasicBlock::TBasicBlockSet::const_iterator referrer = referrers.begin(); referrer != referrers.end(); ++referrer) { - st::TSmalltalkInstruction terminator(0); - bool referrerHasTerminator = (*referrer)->getTerminator(terminator); + H_AreBBsLinked(st::ControlGraph* graph) : st::NodeVisitor(graph) {} + virtual bool visitNode(st::ControlNode& node) { + st::BasicBlock* currentBB = node.getDomain()->getBasicBlock(); + if (st::InstructionNode* inst = node.cast()) { + if (inst->getInstruction().isBranch()) { + st::TSmalltalkInstruction branch = inst->getInstruction(); + const st::TNodeSet& outEdges = node.getOutEdges(); + if (branch.getArgument() == special::branchIfTrue || branch.getArgument() == special::branchIfFalse) { + EXPECT_EQ(outEdges.size(), 2); + if (outEdges.size() == 2) { + st::TNodeSet::const_iterator edgeIter = outEdges.begin(); + st::ControlNode* edge1 = *(edgeIter++); + st::ControlNode* edge2 = *edgeIter; + bool pointToFirst = edge1->getIndex() == branch.getExtra(); + bool pointToSecond = edge2->getIndex() == branch.getExtra(); + EXPECT_TRUE( pointToFirst || pointToSecond ) << "branchIf* must point to one of the edges"; + EXPECT_FALSE( pointToFirst && pointToSecond ) << "branchIf* must point to only one of the edges"; - EXPECT_TRUE(referrerHasTerminator); - EXPECT_TRUE(terminator.isBranch()); - EXPECT_EQ( terminator.getExtra(), domain.getBasicBlock()->getOffset() ) - << "The destination of terminator must be the offset of the first instruction of BB"; + { + SCOPED_TRACE("The BB of outgoing edges must contait current BB"); + st::BasicBlock::TBasicBlockSet& referrers1 = edge1->getDomain()->getBasicBlock()->getReferers(); + EXPECT_NE(referrers1.end(), referrers1.find(currentBB)); + st::BasicBlock::TBasicBlockSet& referrers2 = edge2->getDomain()->getBasicBlock()->getReferers(); + EXPECT_NE(referrers2.end(), referrers2.find(currentBB)); + } + } + } + if (branch.getArgument() == special::branch) { + EXPECT_EQ(outEdges.size(), 1); + if (outEdges.size() == 1) { + st::ControlNode* edge = *(outEdges.begin()); + EXPECT_EQ(edge->getIndex(), branch.getExtra()) + << "Unconditional branch must point exactly to its the only one out edge"; + st::BasicBlock::TBasicBlockSet& referrers = edge->getDomain()->getBasicBlock()->getReferers(); + EXPECT_NE(referrers.end(), referrers.find(currentBB)); + } + } + } } return true; } From 67b527ddba7c370d4bdbcf292df83de722d67716 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 19:49:26 +0300 Subject: [PATCH 190/290] Fixes helper which checks whether a phi has incoming edges Issue: #32 --- tests/helpers/ControlGraph.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 3ff39b1..a539b4f 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -113,10 +113,10 @@ class H_AreBBsLinked: public st::NodeVisitor } }; -class H_CorrectNumOfEdges: public st::NodeVisitor +class H_CorrectNumOfEdges: public st::PlainNodeVisitor { public: - H_CorrectNumOfEdges(st::ControlGraph* graph) : st::NodeVisitor(graph) {} + H_CorrectNumOfEdges(st::ControlGraph* graph) : st::PlainNodeVisitor(graph) {} virtual bool visitNode(st::ControlNode& node) { if (st::InstructionNode* inst = node.cast()) { @@ -168,8 +168,8 @@ class H_CorrectNumOfEdges: public st::NodeVisitor if (st::PhiNode* phi = node.cast()) { const st::TNodeSet& inEdges = phi->getInEdges(); const st::TNodeSet& outEdges = phi->getOutEdges(); - EXPECT_GT(inEdges.size(), 0); - EXPECT_GT(outEdges.size(), 0); + EXPECT_GT(inEdges.size(), 0) << "The phi must consist of at least 1 incoming edge"; + EXPECT_GE(outEdges.size(), 1) << "There must be a node using the given phi"; } if (st::TauNode* tau = node.cast()) { EXPECT_TRUE(tau == NULL /* always fails */); // TODO From 40fb2c155cc757f044acec3d18a723fe9f0f9553 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 21:33:13 +0300 Subject: [PATCH 191/290] Fixes test case checking whether BBs are linked together Issue: #32 --- tests/helpers/ControlGraph.h | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index a539b4f..bc34cf3 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -81,18 +81,25 @@ class H_AreBBsLinked: public st::NodeVisitor EXPECT_EQ(outEdges.size(), 2); if (outEdges.size() == 2) { st::TNodeSet::const_iterator edgeIter = outEdges.begin(); - st::ControlNode* edge1 = *(edgeIter++); - st::ControlNode* edge2 = *edgeIter; - bool pointToFirst = edge1->getIndex() == branch.getExtra(); - bool pointToSecond = edge2->getIndex() == branch.getExtra(); - EXPECT_TRUE( pointToFirst || pointToSecond ) << "branchIf* must point to one of the edges"; - EXPECT_FALSE( pointToFirst && pointToSecond ) << "branchIf* must point to only one of the edges"; + st::ControlNode* target1 = *(edgeIter++); + st::ControlNode* target2 = *edgeIter; + st::ControlDomain* target1Domain = target1->getDomain(); + st::ControlDomain* target2Domain = target2->getDomain(); + EXPECT_EQ(target1, target1Domain->getEntryPoint()); + EXPECT_EQ(target2, target2Domain->getEntryPoint()); + uint16_t target1Offset = target1Domain->getBasicBlock()->getOffset(); + uint16_t target2Offset = target2Domain->getBasicBlock()->getOffset(); + + bool pointToFirst = target1Offset == branch.getExtra(); + bool pointToSecond = target2Offset == branch.getExtra(); + EXPECT_TRUE( pointToFirst || pointToSecond ) << "branchIf* must point to one of the targets"; + EXPECT_FALSE( pointToFirst && pointToSecond ) << "branchIf* must point to only one of the targets"; { - SCOPED_TRACE("The BB of outgoing edges must contait current BB"); - st::BasicBlock::TBasicBlockSet& referrers1 = edge1->getDomain()->getBasicBlock()->getReferers(); + SCOPED_TRACE("The referrers of outgoing edges must contain current BB"); + st::BasicBlock::TBasicBlockSet& referrers1 = target1->getDomain()->getBasicBlock()->getReferers(); EXPECT_NE(referrers1.end(), referrers1.find(currentBB)); - st::BasicBlock::TBasicBlockSet& referrers2 = edge2->getDomain()->getBasicBlock()->getReferers(); + st::BasicBlock::TBasicBlockSet& referrers2 = target2->getDomain()->getBasicBlock()->getReferers(); EXPECT_NE(referrers2.end(), referrers2.find(currentBB)); } } @@ -100,10 +107,13 @@ class H_AreBBsLinked: public st::NodeVisitor if (branch.getArgument() == special::branch) { EXPECT_EQ(outEdges.size(), 1); if (outEdges.size() == 1) { - st::ControlNode* edge = *(outEdges.begin()); - EXPECT_EQ(edge->getIndex(), branch.getExtra()) + st::ControlNode* target = *(outEdges.begin()); + st::ControlDomain* targetDomain = target->getDomain(); + EXPECT_EQ(target, targetDomain->getEntryPoint()); + uint16_t targetOffset = targetDomain->getBasicBlock()->getOffset(); + EXPECT_EQ(targetOffset, branch.getExtra()) << "Unconditional branch must point exactly to its the only one out edge"; - st::BasicBlock::TBasicBlockSet& referrers = edge->getDomain()->getBasicBlock()->getReferers(); + st::BasicBlock::TBasicBlockSet& referrers = target->getDomain()->getBasicBlock()->getReferers(); EXPECT_NE(referrers.end(), referrers.find(currentBB)); } } From cce7243562c8b858411daf97d9fa3145c020b512 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 22:29:55 +0300 Subject: [PATCH 192/290] Adds checker for orphan nodes Issue: #32 --- tests/helpers/ControlGraph.h | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index bc34cf3..85cdcce 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -5,6 +5,8 @@ #include #include +#include + class H_LastInstIsTerminator: public st::BasicBlockVisitor { public: @@ -188,6 +190,68 @@ class H_CorrectNumOfEdges: public st::PlainNodeVisitor } }; +class H_NoOrphans { + st::ControlGraph* m_cfg; +public: + H_NoOrphans(st::ControlGraph* graph) : m_cfg(graph) {} + void check() { + st::TNodeSet linkedNodes = getLinkedNodes(); + st::TNodeSet allNodes = getAllNodes(); + typedef std::vector TOrphans; + TOrphans orphans; + std::set_difference( + linkedNodes.begin(), linkedNodes.end(), + allNodes.begin(), allNodes.end(), + std::back_inserter(orphans) + ); + for (TOrphans::const_iterator it = orphans.begin(); it != orphans.end(); ++it) { + st::ControlNode* node = *it; + std::stringstream ss; + ss << "Orphan node index: " << node->getIndex() + << " from BB offset: " << node->getDomain()->getBasicBlock()->getOffset(); + FAIL() << ss.str(); + } + } +private: + st::TNodeSet getLinkedNodes() const { + class LinkedNodesGetter : public st::NodeVisitor { + public: + st::TNodeSet& m_visitedNodes; + LinkedNodesGetter(st::ControlGraph* graph, st::TNodeSet& outResult) + : st::NodeVisitor(graph), m_visitedNodes(outResult) {} + virtual bool visitNode(st::ControlNode& node) { + m_visitedNodes.insert(&node); + st::TNodeSet outEdges = node.getOutEdges(); + for(st::TNodeSet::const_iterator it = outEdges.begin(); it != outEdges.end(); ++it) { + m_visitedNodes.insert(*it); + } + return true; + } + }; + st::TNodeSet result; + LinkedNodesGetter getter(m_cfg, result); + getter.run(); + return result; + } + st::TNodeSet getAllNodes() const { + class AllNodesGetter : public st::PlainNodeVisitor { + public: + st::TNodeSet& m_visitedNodes; + AllNodesGetter(st::ControlGraph* graph, st::TNodeSet& outResult) + : st::PlainNodeVisitor(graph), m_visitedNodes(outResult) {} + virtual bool visitNode(st::ControlNode& node) { + m_visitedNodes.insert(&node); + return true; + } + }; + st::TNodeSet result; + AllNodesGetter getter(m_cfg, result); + getter.run(); + return result; + } +}; + + void H_CheckCFGCorrect(st::ControlGraph* graph) { { @@ -206,6 +270,10 @@ void H_CheckCFGCorrect(st::ControlGraph* graph) H_CorrectNumOfEdges visitor(graph); visitor.run(); } + { + H_NoOrphans checker(graph); + checker.check(); + } } #endif From d8ff7fbfd8166378fa805fd1b4bf24b784cfb065 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Feb 2015 23:17:17 +0300 Subject: [PATCH 193/290] Fixes helper to check the number of arguments of a primitive Issue: #32 --- tests/helpers/ControlGraph.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 85cdcce..235f99c 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -168,6 +168,9 @@ class H_CorrectNumOfEdges: public st::PlainNodeVisitor break; } } break; + case opcode::doPrimitive: + EXPECT_EQ(inst->getInstruction().getArgument(), inst->getArgumentsCount()); + break; default: EXPECT_GE( inst->getArgumentsCount(), 1); break; From 33798076f3730c0689e4c900157eb8437ddeee1c Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 12 Apr 2015 18:41:49 +0600 Subject: [PATCH 194/290] Comments out unused sizeInByte constants (lifetime TODO) Issue: #32 --- src/MethodCompiler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index cf037e7..4c81956 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -1073,7 +1073,7 @@ void MethodCompiler::doSendBinary(TJITContext& jit) // Now creating the argument array TObjectAndSize array = createArray(jit, 2); Value* const argumentsObject = array.first; - const uint32_t sizeInBytes = array.second; // TODO lifetime + // const uint32_t sizeInBytes = array.second; // TODO lifetime jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(0), leftValue); jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(1), rightValue); @@ -1895,7 +1895,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, TObjectAndSize array = createArray(jit, argumentsCount); Value* const argumentsObject = array.first; - const uint32_t sizeInBytes = array.second; // TODO lifetime + // const uint32_t sizeInBytes = array.second; // TODO lifetime // Filling object with contents uint8_t index = argumentsCount; From 59b1f31ac512729d66fe86a0d7f78b1c37ffa5a8 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Feb 2015 00:09:51 +0300 Subject: [PATCH 195/290] Checks whether all BBs have referrers Issue: #32 --- tests/helpers/ControlGraph.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 235f99c..4bd8179 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -73,6 +73,28 @@ class H_AreBBsLinked: public st::NodeVisitor { public: H_AreBBsLinked(st::ControlGraph* graph) : st::NodeVisitor(graph) {} + virtual bool visitDomain(st::ControlDomain& domain) { + st::BasicBlock* currentBB = domain.getBasicBlock(); + if (currentBB->getOffset() != 0) { + std::stringstream ss; + ss << "BB offset: " << currentBB->getOffset(); + SCOPED_TRACE(ss.str()); + EXPECT_GT(currentBB->getReferers().size(), 0) + << "All BB but the 1st must have referrers"; + } + st::ControlDomain::iterator iNode = domain.begin(); + const st::ControlDomain::iterator iEnd = domain.end(); + + if (iNode != iEnd) { + while (iNode != iEnd) { + if (! visitNode(** iNode)) + return false; + ++iNode; + } + } + + return true; + } virtual bool visitNode(st::ControlNode& node) { st::BasicBlock* currentBB = node.getDomain()->getBasicBlock(); if (st::InstructionNode* inst = node.cast()) { From fec84392cd0d41fd90b5eb36495fd51202611ecc Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 11 Apr 2015 13:08:43 +0600 Subject: [PATCH 196/290] Adds logic that removes redundant phis Issue: #32 --- src/ControlGraph.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 87f9c01..2cd4bb4 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -270,6 +270,7 @@ class GraphLinker : public NodeVisitor { void mergePhi(PhiNode* source, PhiNode* target); ControlNode* getRequestedNode(ControlDomain* domain, std::size_t index); + ControlNode* optimizePhi(PhiNode* phi); ControlDomain* m_currentDomain; ControlNode* m_nodeToLink; @@ -371,6 +372,43 @@ void GraphLinker::mergePhi(PhiNode* source, PhiNode* target) m_graph->eraseNode(source); } +ControlNode* GraphLinker::optimizePhi(PhiNode* phi) +{ + TNodeSet incomingValues; + + const PhiNode::TIncomingList& incomings = phi->getIncomingList(); + assert(incomings.size() > 1); // Phi should have at least two incoming edges + + for (size_t index = 0; index < incomings.size(); index++) + incomingValues.insert(incomings[index].node); + + assert(incomingValues.size()); + + if (traces_enabled) + std::printf("GraphLinker::optimizePhi : phi node %u has %u unique incoming values\n", phi->getIndex(), incomingValues.size()); + + if (incomingValues.size() > 1) + return phi; // Phi is ok, no need to optimize. Leave everything as is. + + // It seem that phi node is redundant becasue all of it's incomings link to the same value. + // This may happen in a diamond-shaped reference diagram if incoming value stored in the top + // domain and consumed in the bottom one. Left and right domains are not affected. + // We may safely remove the phi because incoming value dominates it's consumer. + + if (traces_enabled) + std::printf("GraphLinker::optimizePhi : phi node %u is redundant and may be removed\n", phi->getIndex()); + + // This is the real value that should be returned + ControlNode* const value = *incomingValues.begin(); + + // Unlink and erase phi + value->removeConsumer(phi); + value->removeEdge(phi); + m_graph->eraseNode(phi); + + return value; +} + ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t argumentIndex) { const BasicBlock::TBasicBlockSet& refererBlocks = domain->getBasicBlock()->getReferers(); @@ -426,6 +464,9 @@ ControlNode* GraphLinker::getRequestedNode(ControlDomain* domain, std::size_t ar } } + if (!singleReferer) + result = optimizePhi(result->cast()); + assert(result); return result; } @@ -466,6 +507,20 @@ class GraphOptimizer : public PlainNodeVisitor { } } } else if (PhiNode* const phi = node.cast()) { + assert(incomings.size()); + + // We may remove the phi node if all of it's incoming entries map to the same node + /*const PhiNode::TIncomingList& incomings = phi->getIncomingList(); + const ControlNode* const testNode = incomings[0].node; + bool unique = true; + + for (std::size_t index = 1; index < incomings.size(); index++) { + if (incomings[index].node != testNode) { + unique = false; + break; + } + }*/ + if (phi->getInEdges().size() == 1) { if (traces_enabled) std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", phi->getIndex()); From aa0e4a0eda4dbb8a6d3373dcc97462d138bc5138 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Feb 2015 01:54:44 +0300 Subject: [PATCH 197/290] Removes stringstream(to format message) from tests Issue: #32 --- tests/helpers/ControlGraph.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 4bd8179..34f55a5 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -76,11 +76,9 @@ class H_AreBBsLinked: public st::NodeVisitor virtual bool visitDomain(st::ControlDomain& domain) { st::BasicBlock* currentBB = domain.getBasicBlock(); if (currentBB->getOffset() != 0) { - std::stringstream ss; - ss << "BB offset: " << currentBB->getOffset(); - SCOPED_TRACE(ss.str()); EXPECT_GT(currentBB->getReferers().size(), 0) - << "All BB but the 1st must have referrers"; + << "All BB but the 1st must have referrers. " + << "BB offset: " << currentBB->getOffset(); } st::ControlDomain::iterator iNode = domain.begin(); const st::ControlDomain::iterator iEnd = domain.end(); @@ -231,10 +229,8 @@ class H_NoOrphans { ); for (TOrphans::const_iterator it = orphans.begin(); it != orphans.end(); ++it) { st::ControlNode* node = *it; - std::stringstream ss; - ss << "Orphan node index: " << node->getIndex() + FAIL() << "Orphan node index: " << node->getIndex() << " from BB offset: " << node->getDomain()->getBasicBlock()->getOffset(); - FAIL() << ss.str(); } } private: From 3e5773675197b49928c622e71bb827d962029aa6 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 8 Feb 2015 15:36:49 +0300 Subject: [PATCH 198/290] Adds fixes after rebase Issue: #32 --- tests/CMakeLists.txt | 5 ----- tests/patterns/DecodeBytecode.h | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d60d3b0..8f1bc2a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,11 +5,6 @@ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) macro(cxx_test pretty_name bin_name sources libs) add_executable(${bin_name} EXCLUDE_FROM_ALL ${sources}) - if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - # this is a 64-bit OS - set_target_properties(${bin_name} PROPERTIES - COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") - endif() target_link_libraries(${bin_name} ${libs} ${GTEST_BOTH_LIBRARIES}) set_target_properties(${bin_name} PROPERTIES COMPILE_DEFINITIONS TESTS_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\") add_test(${pretty_name} ${bin_name}) diff --git a/tests/patterns/DecodeBytecode.h b/tests/patterns/DecodeBytecode.h index 7c4a607..1434a4f 100644 --- a/tests/patterns/DecodeBytecode.h +++ b/tests/patterns/DecodeBytecode.h @@ -5,7 +5,7 @@ #include #include -void __assert_fail(const char *__assertion, const char *__file, unsigned int __line, const char *__function) +void __assert_fail(const char *__assertion, const char *__file, unsigned int __line, const char */*__function*/) { std::stringstream ss; ss << "Assertion '" << __assertion << "' failed in '" << __file << "' at: " << __line; From 15e07a55df0cf1502baf151e4c4f20d5b7709ec8 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 8 Feb 2015 15:42:09 +0300 Subject: [PATCH 199/290] Adds newline at EOF in ParsedBytecode.cpp Issue: #32 --- src/ParsedBytecode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 90ec481..d29e98d 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -299,4 +299,4 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* if (traces_enabled) std::printf("%.4u : linking blocks %p (%u) -> %p (%u) with branch instruction\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), nextBlock, nextBlock->getOffset()); } -} \ No newline at end of file +} From e7f3aba17914d93b8e2e0b3b7609233e4bcf3ea2 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 13 Feb 2015 00:18:11 +0300 Subject: [PATCH 200/290] Adds test to check whether instructions linked with value providers Issue: #32 --- tests/helpers/ControlGraph.h | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 34f55a5..a00d978 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -272,6 +272,39 @@ class H_NoOrphans { } }; +class H_ConsumeProvider: public st::PlainNodeVisitor +{ +public: + H_ConsumeProvider(st::ControlGraph* graph) : st::PlainNodeVisitor(graph) {} + virtual bool visitNode(st::ControlNode& node) { + if (st::InstructionNode* instNode = node.cast()) { + st::TSmalltalkInstruction inst = instNode->getInstruction(); + if (inst.isValueConsumer()) { + // Each consumer must consume value + std::size_t argSize = instNode->getArgumentsCount(); + EXPECT_GT(argSize, 0); + for(std::size_t i = 0; i < argSize; ++i) { + st::ControlNode* argNode = instNode->getArgument(i); + if (st::InstructionNode* arg = argNode->cast()) { + EXPECT_TRUE(arg->getInstruction().isValueProvider()) + << "'" << arg->getInstruction().toString() << "' should provide value for '" << inst.toString() << "'"; + } + } + } + } + if (st::PhiNode* phiNode = node.cast()) { + const st::TNodeSet& inEdges = phiNode->getInEdges(); + for(st::TNodeSet::const_iterator it = inEdges.begin(); it != inEdges.end(); ++it) { + st::ControlNode* edge = *it; + if (st::InstructionNode* inst = edge->cast()) { + EXPECT_TRUE(inst->getInstruction().isValueProvider()) + << "'" << inst->getInstruction().toString() << "' should provide value for 'Phi index: " << phiNode->getPhiIndex() << "'"; + } + } + } + return true; + } +}; void H_CheckCFGCorrect(st::ControlGraph* graph) { @@ -295,6 +328,10 @@ void H_CheckCFGCorrect(st::ControlGraph* graph) H_NoOrphans checker(graph); checker.check(); } + { + H_ConsumeProvider visitor(graph); + visitor.run(); + } } #endif From 1b980ce69a51c6af3cdd851f7da32007ac1ba9be Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 13 Feb 2015 00:35:58 +0300 Subject: [PATCH 201/290] Adds tests which checks whether branches jump not on terminators Issue: #32 --- tests/helpers/ControlGraph.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index a00d978..c729583 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -306,6 +306,37 @@ class H_ConsumeProvider: public st::PlainNodeVisitor } }; +class H_BranchJumpsOnCorrectNode: public st::NodeVisitor +{ +public: + H_BranchJumpsOnCorrectNode(st::ControlGraph* graph) : st::NodeVisitor(graph) {} + virtual bool visitNode(st::ControlNode& node) { + if (st::InstructionNode* instNode = node.cast()) { + st::TSmalltalkInstruction inst = instNode->getInstruction(); + if (inst.isBranch()) { + const st::TNodeSet& outEdges = instNode->getOutEdges(); + for(st::TNodeSet::const_iterator it = outEdges.begin(); it != outEdges.end(); ++it) { + st::ControlNode* outNode = *it; + if (outNode->cast()) { + st::TSmalltalkInstruction outInst = outNode->cast()->getInstruction(); + + if (inst.getArgument() == special::branch) { + SCOPED_TRACE("Unconditional branches shouldn't jump on terminators"); + EXPECT_FALSE(outInst.isTerminator()) + << "'" << inst.toString() << "' shouldn't jump on '" << outInst.toString() << "'"; + } else { + SCOPED_TRACE("Conditional branches shouldn't jump on branches"); + EXPECT_FALSE(outInst.isBranch()) + << "'" << inst.toString() << "' shouldn't jump on '" << outInst.toString() << "'"; + } + } + } + } + } + return true; + } +}; + void H_CheckCFGCorrect(st::ControlGraph* graph) { { @@ -332,6 +363,10 @@ void H_CheckCFGCorrect(st::ControlGraph* graph) H_ConsumeProvider visitor(graph); visitor.run(); } + { + H_BranchJumpsOnCorrectNode visitor(graph); + visitor.run(); + } } #endif From d3bcf5cfe658e9fd506d0155b79ca5e5d3a3f03f Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 13 Feb 2015 01:29:10 +0300 Subject: [PATCH 202/290] Fixes test for TSmalltalkInstruction (selfReturn doesn't consume value) Issue: #32 --- tests/tsmalltalk_instruction.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/tsmalltalk_instruction.cpp b/tests/tsmalltalk_instruction.cpp index 5555160..4cdd7dc 100644 --- a/tests/tsmalltalk_instruction.cpp +++ b/tests/tsmalltalk_instruction.cpp @@ -53,6 +53,7 @@ TEST(TSmalltalkInstruction, isValueConsumer) SCOPED_TRACE("conditional branches are consumers"); EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).isValueConsumer()); EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).isValueConsumer()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branch).isValueConsumer()); } { SCOPED_TRACE("insts dealing with messages are consumers"); @@ -71,6 +72,7 @@ TEST(TSmalltalkInstruction, isValueConsumer) EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::popTop).isValueConsumer()); EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::stackReturn).isValueConsumer()); EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).isValueConsumer()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::selfReturn).isValueConsumer()); } } From 82671612f201bb491a0ba5fc511dcdba69dddb7e Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 13 Feb 2015 01:29:56 +0300 Subject: [PATCH 203/290] Fixes TSmalltalkInstruction::isValueConsumer (selfReturn doesn't consume value) Issue: #32 --- src/TSmalltalkInstruction.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index f5360f9..71ed56a 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -149,8 +149,13 @@ bool st::TSmalltalkInstruction::isValueConsumer() const { return true; case opcode::doSpecial: - // All other specials consume a value - return m_argument != special::branch; + switch (m_argument) { + case special::branch: + case special::selfReturn: + return false; + default: + return true; + } case opcode::doPrimitive: // All system primitives consume a value From a60b42e10e74ecccd527f500359ef7646afc452e Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 13 Feb 2015 16:42:35 +0300 Subject: [PATCH 204/290] Fixes test for TSmalltalkInstruction (primitives are value providers) Issue: #32 --- tests/tsmalltalk_instruction.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/tsmalltalk_instruction.cpp b/tests/tsmalltalk_instruction.cpp index 4cdd7dc..ae210df 100644 --- a/tests/tsmalltalk_instruction.cpp +++ b/tests/tsmalltalk_instruction.cpp @@ -42,9 +42,25 @@ TEST(TSmalltalkInstruction, isValueProvider) SCOPED_TRACE("some other insts are not providers either"); EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignTemporary).isValueProvider()); EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignInstance).isValueProvider()); - EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doPrimitive).isValueProvider()); EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::popTop).isValueProvider()); } + { + SCOPED_TRACE("the other are providers"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doPrimitive).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::duplicate).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::markArguments).isValueProvider()); + + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushInstance).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushArgument).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushTemporary).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushLiteral).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushBlock).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushConstant).isValueProvider()); + + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendMessage).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendUnary).isValueProvider()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendBinary).isValueProvider()); + } } TEST(TSmalltalkInstruction, isValueConsumer) From 949c8c0efa1a2b7fedb5c93e5a429fe6768f3f04 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 13 Feb 2015 16:43:26 +0300 Subject: [PATCH 205/290] Fixes TSmalltalkInstruction::isValueProvider (primitives provide values) Issue: #32 --- src/TSmalltalkInstruction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 71ed56a..1b0c905 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -48,11 +48,11 @@ bool st::TSmalltalkInstruction::isValueProvider() const { case opcode::sendMessage: case opcode::sendUnary: case opcode::sendBinary: + case opcode::doPrimitive: return true; case opcode::assignTemporary: case opcode::assignInstance: - case opcode::doPrimitive: // ? return false; case opcode::doSpecial: From f913d99cbff524d86c5de42cf64195b3249c6b65 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 03:02:25 +0300 Subject: [PATCH 206/290] Adds tests for duplicate opcode Issue: #32 --- image/imageSource.st | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index 8aeb582..4eb5d23 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -168,6 +168,10 @@ METHOD Object ^ (self == arg) not ! METHOD Object +yourself + ^self +! +METHOD Object class <2 self> ! @@ -1655,6 +1659,13 @@ pushConstants |testBlock| ['9' asNumber ] assertEq: 9 withComment: '9'. ! +METHOD StatementsTest +duplicate |x| + [ x <- '42'; yourself ] assertEq: '42' withComment: '0'. + [ x <- '42'; asNumber; yourself ] assertEq: 42 withComment: '1'. + [ x <- '42'. (x; yourself) = x ] assertWithComment: '2'. +! + METHOD Undefined sortTest | list | list <- List new. @@ -1762,8 +1773,12 @@ runAllTests ArrayTest new runAll. PrimitiveTest new runAll. MagnitudeTest new runAll. + "CompareTest new runAll." + StringTest new runAll. ListTest new runAll. MethodLookupTest new runAll. + StatementsTest new runAll. + BranchTest new runAll. ! @@ -1814,10 +1829,12 @@ factTest: x METHOD Undefined main | command data x | Char initialize. - Class fillChildren. - System fixMethodClasses. + "Class fillChildren." + "System fixMethodClasses." "Jit do: [ nil inlineTest ]." + " Jit do: [ StatementsTest new runAll; runAll. ]. " + "Scheduler initialize. Thread run: [ 1 to: 10 do: [ :x | 'A' printNl ] ]. Thread run: [ 1 to: 10 do: [ :x | 'B' printNl ] ]. From 47ff2013fcd1bdc9c8c5548c5456c88be85ecedc Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Mar 2015 23:40:18 +0300 Subject: [PATCH 207/290] Removes check whether branch should jump on branches Issue: #32 --- tests/helpers/ControlGraph.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index c729583..45e1978 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -364,8 +364,8 @@ void H_CheckCFGCorrect(st::ControlGraph* graph) visitor.run(); } { - H_BranchJumpsOnCorrectNode visitor(graph); - visitor.run(); + // H_BranchJumpsOnCorrectNode visitor(graph); + // visitor.run(); } } From e72f452cc7196529045f6fd503ca856b48304590 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Mar 2015 23:40:42 +0300 Subject: [PATCH 208/290] Fixes segfault in stack_semantics Issue: #32 --- tests/stack_semantics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/stack_semantics.cpp b/tests/stack_semantics.cpp index f626e6e..4a6d9bf 100644 --- a/tests/stack_semantics.cpp +++ b/tests/stack_semantics.cpp @@ -47,8 +47,9 @@ TEST_P(P_DecodeBytecode, StackSemanticsTemps) SCOPED_TRACE("First arg must be PushTemporary"); EXPECT_EQ( firstArg->getNodeType(), st::ControlNode::ntInstruction ); st::InstructionNode* pushTemp = firstArg->cast(); - EXPECT_TRUE(pushTemp); - EXPECT_EQ(pushTemp->getInstruction().getOpcode(), opcode::pushTemporary); + EXPECT_TRUE(pushTemp != NULL); + if (pushTemp) + EXPECT_EQ(pushTemp->getInstruction().getOpcode(), opcode::pushTemporary); } return false; From 0e009fac00a9f08f2b0157f99f514de847796105 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 6 Mar 2015 23:50:30 +0300 Subject: [PATCH 209/290] Fixes the correct order of arguments for EXPECT_* Issue: #32 --- tests/abab_problem.cpp | 12 ++++++------ tests/stack_semantics.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/abab_problem.cpp b/tests/abab_problem.cpp index 4db393e..2223082 100644 --- a/tests/abab_problem.cpp +++ b/tests/abab_problem.cpp @@ -30,19 +30,19 @@ void checkSendBinaryArg(st::ControlNode* arg) { { SCOPED_TRACE("Each argument of sendBinary is a phi node"); - EXPECT_EQ( arg->getNodeType(), st::ControlNode::ntPhi); + ASSERT_EQ( st::ControlNode::ntPhi, arg->getNodeType()); } { SCOPED_TRACE("Each argument of sendBinary has 2 egdes"); - EXPECT_EQ( arg->getInEdges().size(), 2); + ASSERT_EQ( 2, arg->getInEdges().size() ); } { SCOPED_TRACE("Each edge of arg-phi is a PushConstant"); for(st::TNodeSet::iterator i = arg->getInEdges().begin(); i != arg->getInEdges().end(); i++) { st::ControlNode* edge = *i; - EXPECT_EQ( edge->getNodeType(), st::ControlNode::ntInstruction ); + ASSERT_EQ( st::ControlNode::ntInstruction, edge->getNodeType() ); if (st::InstructionNode* edgeInst = edge->cast()) { - EXPECT_EQ( edgeInst->getInstruction().getOpcode(), opcode::pushConstant ); + ASSERT_EQ( opcode::pushConstant, edgeInst->getInstruction().getOpcode() ); } } } @@ -62,8 +62,8 @@ TEST_P(P_DecodeBytecode, ABAB) { sendBinaryFound = true; - EXPECT_EQ( inst->getInEdges().size(), 4); // 2 branches + 2 phis - EXPECT_EQ( inst->getArgumentsCount(), 2); + EXPECT_EQ( 4, inst->getInEdges().size()); // 2 branches + 2 phis + EXPECT_EQ( 2, inst->getArgumentsCount() ); st::ControlNode* firstArg = inst->getArgument(0); st::ControlNode* secondArg = inst->getArgument(1); EXPECT_NE(firstArg, secondArg); diff --git a/tests/stack_semantics.cpp b/tests/stack_semantics.cpp index 4a6d9bf..281cc08 100644 --- a/tests/stack_semantics.cpp +++ b/tests/stack_semantics.cpp @@ -41,15 +41,15 @@ TEST_P(P_DecodeBytecode, StackSemanticsTemps) { SCOPED_TRACE("Second arg must be phi"); - EXPECT_EQ( secondArg->getNodeType(), st::ControlNode::ntPhi ); + EXPECT_EQ( st::ControlNode::ntPhi, secondArg->getNodeType() ); } { SCOPED_TRACE("First arg must be PushTemporary"); - EXPECT_EQ( firstArg->getNodeType(), st::ControlNode::ntInstruction ); + EXPECT_EQ( st::ControlNode::ntInstruction, firstArg->getNodeType() ); st::InstructionNode* pushTemp = firstArg->cast(); EXPECT_TRUE(pushTemp != NULL); if (pushTemp) - EXPECT_EQ(pushTemp->getInstruction().getOpcode(), opcode::pushTemporary); + EXPECT_EQ( opcode::pushTemporary, pushTemp->getInstruction().getOpcode() ); } return false; From 0543f76b55df3a15529c2bb6fa55efa7e3e80c99 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 8 Mar 2015 04:39:56 +0300 Subject: [PATCH 210/290] Adds test to check behaviour of graph builder on stack underflow Issue: #32 --- tests/CMakeLists.txt | 1 + tests/stack_underflow.cpp | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/stack_underflow.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8f1bc2a..4cb7eab 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,4 +18,5 @@ cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;-pthread") cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;-pthread") cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;-pthread") +cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi;-pthread") cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set;-pthread") diff --git a/tests/stack_underflow.cpp b/tests/stack_underflow.cpp new file mode 100644 index 0000000..678292a --- /dev/null +++ b/tests/stack_underflow.cpp @@ -0,0 +1,22 @@ +#include "patterns/DecodeBytecode.h" +#include "helpers/ControlGraph.h" + +static const uint8_t UnderflowBytecode[] = { + 178, // SendBinary + + 178, // SendBinary + + 178, // SendBinary + + 178, // SendBinary + + 242 // DoSpecial stackReturn +}; + +INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, + ::testing::Values( std::tr1::make_tuple( + std::string("UnderflowBytecode"), + std::string(reinterpret_cast(UnderflowBytecode), sizeof(UnderflowBytecode))) ) +); + +TEST_P(P_DecodeBytecode, Underflow) { + // TODO: do nothing but check err msg + H_CorrectNumOfEdges check(m_cfg); + check.run(); +} From a3b884b2afd53a6bbf7e956beefaebe7b23f8e0d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Apr 2015 21:42:15 +0300 Subject: [PATCH 211/290] Fixes ControlGraphVisualizer 1. It throws exception if file can't be created 2. It escapes chars in filename 3. If literals can't be accessed, it generates a name for the node by default Issue: #32 --- include/visualization.h | 7 +---- src/ControlGraphVisualizer.cpp | 50 ++++++++++++++++++++++++++++++++-- src/MethodCompiler.cpp | 8 ++---- src/main.cpp | 2 +- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/include/visualization.h b/include/visualization.h index 01f3cd0..a0cd17d 100644 --- a/include/visualization.h +++ b/include/visualization.h @@ -10,12 +10,7 @@ class ControlGraphVisualizer : public st::PlainNodeVisitor { public: - ControlGraphVisualizer(st::ControlGraph* graph, const std::string& fileName) : st::PlainNodeVisitor(graph) { - m_stream.open(fileName.c_str(), std::ios::out | std::ios::trunc); - - m_stream << "digraph G2 {\n"; - firstDomain = true; - } + ControlGraphVisualizer(st::ControlGraph* graph, const std::string& fileName, const std::string& directory = "."); virtual ~ControlGraphVisualizer() { finish(); } diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 5087b4a..6aded5b 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -1,5 +1,43 @@ #include +#include +#include + +std::string escape_path(const std::string& path) { + if (path.empty()) + return ""; + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; + + for (std::size_t i = 0; i < path.size(); ++i) { + std::string::value_type c = path[i]; + + if (iscntrl(c) || c == '/') { + escaped << '%' << std::setw(2) << (int) c; + } else { + escaped << c; + } + } + + return escaped.str(); +} + +ControlGraphVisualizer::ControlGraphVisualizer(st::ControlGraph* graph, const std::string& fileName, const std::string& directory /*= "."*/) + : st::PlainNodeVisitor(graph) +{ + std::string fullpath = directory + "/" + escape_path(fileName) + ".dot"; + m_stream.open(fullpath.c_str(), std::ios::out | std::ios::trunc); + if (m_stream.fail()) { + std::stringstream ss; + ss << "Cannot open/truncate '" << fullpath << "' in '" << get_current_dir_name() << "'"; + throw std::ios_base::failure( ss.str() ); + } + m_stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + m_stream << "digraph G2 {\n"; + firstDomain = true; +} bool ControlGraphVisualizer::visitDomain(st::ControlDomain& /*domain*/) { // if (!firstDomain) @@ -113,8 +151,16 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { if (instruction->getInstruction().getOpcode() == opcode::sendMessage) { TSymbolArray* const literals = m_graph->getParsedMethod()->getOrigin()->literals; - TSymbol* const name = literals->getField(instruction->getInstruction().getArgument()); - label += " " + name->toString(); + uint32_t litIdx = instruction->getInstruction().getArgument(); + std::string name; + if (literals) + name = literals->getField(litIdx)->toString(); + else { + std::stringstream ss; + ss << "lit" << litIdx; + name = ss.str(); + } + label += " " + name; } else if (instruction->getInstruction().isBranch()) { std::stringstream ss; ss << " " << instruction->getInstruction().getExtra(); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 4c81956..a1ab815 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -443,9 +443,7 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF outs() << "Compiling " << jit.function->getName() << "\n"; // { -// std::ostringstream ss; -// ss << "dots/" << jit.function->getName().data() << ".dot"; -// ControlGraphVisualizer vis(jit.controlGraph, ss.str()); +// ControlGraphVisualizer vis(jit.controlGraph, jit.function->getName(), "dots/"); // vis.run(); // } @@ -765,9 +763,7 @@ void MethodCompiler::doPushBlock(TJITContext& jit) outs() << "Compiling block " << blockFunctionName << "\n"; // { -// std::ostringstream ss; -// ss << "dots/" << blockFunctionName << ".dot"; -// ControlGraphVisualizer vis(blockContext.controlGraph, ss.str()); +// ControlGraphVisualizer vis(blockContext.controlGraph, blockFunctionName, "dots/"); // vis.run(); // } diff --git a/src/main.cpp b/src/main.cpp index 7bc5ef4..91e07c9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,7 +56,7 @@ void testControlGraph(Image* image) { st::ControlGraph controlGraph(&parsedMethod); controlGraph.buildGraph(); - ControlGraphVisualizer visualizer(&controlGraph, "graph.dot"); + ControlGraphVisualizer visualizer(&controlGraph, "graph"); visualizer.run(); } From 9be6c7e954c2936a6ed7d488611b3dabb70c1671 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Apr 2015 21:44:46 +0300 Subject: [PATCH 212/290] Fixes ABAB test due to new requirements Issue: #32 --- tests/abab_problem.cpp | 47 +++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/tests/abab_problem.cpp b/tests/abab_problem.cpp index 2223082..0ffe767 100644 --- a/tests/abab_problem.cpp +++ b/tests/abab_problem.cpp @@ -26,23 +26,44 @@ INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, ::testing::Values( std::tr1::make_tuple(std::string("Bytecode for ABAB"), std::string(reinterpret_cast(ABABbytecode), sizeof(ABABbytecode))) ) ); -void checkSendBinaryArg(st::ControlNode* arg) +void checkSendBinaryArg(st::InstructionNode* inst, int idx) { + st::ControlNode* arg = inst->getArgument(idx); { SCOPED_TRACE("Each argument of sendBinary is a phi node"); ASSERT_EQ( st::ControlNode::ntPhi, arg->getNodeType()); } + st::PhiNode* phiArg = arg->cast(); + + std::vector phisToCheck; + switch (idx) { - SCOPED_TRACE("Each argument of sendBinary has 2 egdes"); - ASSERT_EQ( 2, arg->getInEdges().size() ); + case 0: { + // The first arg is a phi containing phis >< + ASSERT_EQ(2, phiArg->getInEdges().size()); + st::ControlNode* phi1 = * phiArg->getInEdges().begin(); + st::ControlNode* phi2 = * ++ phiArg->getInEdges().begin(); + phisToCheck.push_back(phi1->cast()); + phisToCheck.push_back(phi2->cast()); + } break; + case 1: { + phisToCheck.push_back(phiArg); + } break; + default: + FAIL() << "idx should be 0 or 1"; } + ASSERT_GT(phisToCheck.size(), 0); + { - SCOPED_TRACE("Each edge of arg-phi is a PushConstant"); - for(st::TNodeSet::iterator i = arg->getInEdges().begin(); i != arg->getInEdges().end(); i++) { - st::ControlNode* edge = *i; - ASSERT_EQ( st::ControlNode::ntInstruction, edge->getNodeType() ); - if (st::InstructionNode* edgeInst = edge->cast()) { - ASSERT_EQ( opcode::pushConstant, edgeInst->getInstruction().getOpcode() ); + for(std::size_t phiIdx = 0; phiIdx < phisToCheck.size(); phiIdx++) { + SCOPED_TRACE("Each edge of arg-phi is a PushConstant"); + st::PhiNode* phi = phisToCheck[phiIdx]; + for(st::TNodeSet::iterator i = phi->getInEdges().begin(); i != phi->getInEdges().end(); i++) { + st::ControlNode* edge = *i; + ASSERT_EQ( st::ControlNode::ntInstruction, edge->getNodeType() ); + if (st::InstructionNode* edgeInst = edge->cast()) { + ASSERT_EQ( opcode::pushConstant, edgeInst->getInstruction().getOpcode() ); + } } } } @@ -64,17 +85,15 @@ TEST_P(P_DecodeBytecode, ABAB) EXPECT_EQ( 4, inst->getInEdges().size()); // 2 branches + 2 phis EXPECT_EQ( 2, inst->getArgumentsCount() ); - st::ControlNode* firstArg = inst->getArgument(0); - st::ControlNode* secondArg = inst->getArgument(1); - EXPECT_NE(firstArg, secondArg); + EXPECT_NE(inst->getArgument(0), inst->getArgument(1)); { SCOPED_TRACE("Check first arg"); - checkSendBinaryArg(firstArg); + checkSendBinaryArg(inst, 0); } { SCOPED_TRACE("Check second arg"); - checkSendBinaryArg(secondArg); + checkSendBinaryArg(inst, 1); } return false; From 0c7606ed02ad74b9586a381061c8634ca13d80e4 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Apr 2015 22:01:39 +0300 Subject: [PATCH 213/290] Fixes tests due to new requirements Issue: #32 --- tests/helpers/ControlGraph.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 45e1978..f60e659 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -195,15 +195,15 @@ class H_CorrectNumOfEdges: public st::PlainNodeVisitor EXPECT_GE( inst->getArgumentsCount(), 1); break; } - if (inst->getInstruction().isValueProvider()) { + if (inst->getInstruction().isValueProvider() && inst->getInstruction().getOpcode() != opcode::pushBlock) { const st::TNodeSet& consumers = inst->getConsumers(); EXPECT_GT(consumers.size(), 0); } } if (st::PhiNode* phi = node.cast()) { - const st::TNodeSet& inEdges = phi->getInEdges(); + const st::PhiNode::TIncomingList& incomingList = phi->getIncomingList(); const st::TNodeSet& outEdges = phi->getOutEdges(); - EXPECT_GT(inEdges.size(), 0) << "The phi must consist of at least 1 incoming edge"; + EXPECT_GT(incomingList.size(), 0) << "The phi must have at least 1 incoming edge"; EXPECT_GE(outEdges.size(), 1) << "There must be a node using the given phi"; } if (st::TauNode* tau = node.cast()) { @@ -280,9 +280,8 @@ class H_ConsumeProvider: public st::PlainNodeVisitor if (st::InstructionNode* instNode = node.cast()) { st::TSmalltalkInstruction inst = instNode->getInstruction(); if (inst.isValueConsumer()) { - // Each consumer must consume value + SCOPED_TRACE(inst.toString()); std::size_t argSize = instNode->getArgumentsCount(); - EXPECT_GT(argSize, 0); for(std::size_t i = 0; i < argSize; ++i) { st::ControlNode* argNode = instNode->getArgument(i); if (st::InstructionNode* arg = argNode->cast()) { From 867f724a3ecc8ed1d28cb2d19c46fb14d977327d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Apr 2015 22:09:49 +0300 Subject: [PATCH 214/290] Fixes unregistered methods in image Issue: #32 --- image/imageSource.st | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index 4eb5d23..fb27e0a 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -1829,8 +1829,8 @@ factTest: x METHOD Undefined main | command data x | Char initialize. - "Class fillChildren." - "System fixMethodClasses." + Class fillChildren. + System fixMethodClasses. "Jit do: [ nil inlineTest ]." " Jit do: [ StatementsTest new runAll; runAll. ]. " From 9a876c105a6b55667b0b6e9b6276625ac229ae37 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 05:34:17 +0300 Subject: [PATCH 215/290] Rewrites `@getObjectFields' using `@getObjectFieldPtr' Issue: #32 --- include/Core.ll | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/Core.ll b/include/Core.ll index 4391424..cd065fa 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -237,25 +237,23 @@ is_object: ret %TClass* %class } - define %TObject* @setObjectClass(%TObject* %this, %TClass* %class) { %addr = call %TClass** @getObjectClassPtr(%TObject* %this) store %TClass* %class, %TClass** %addr ret %TObject* %this } -define %TObject** @getObjectFields(%TObject* %this) { - %fields = getelementptr inbounds %TObject* %this, i32 0, i32 2 - %result = getelementptr inbounds [0 x %TObject*]* %fields, i32 0, i32 0 - ret %TObject** %result -} - define %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) { %fields = getelementptr inbounds %TObject* %object, i32 0, i32 2 %fieldPtr = getelementptr inbounds [0 x %TObject*]* %fields, i32 0, i32 %index ret %TObject** %fieldPtr } +define %TObject** @getObjectFields(%TObject* %this) { + %fieldsPtr = call %TObject** @getObjectFieldPtr(%TObject* %this, i32 0) + ret %TObject** %fieldsPtr +} + define %TObject* @getObjectField(%TObject* %object, i32 %index) { %fieldPtr = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) %result = load %TObject** %fieldPtr From 2c1bb291036c7f54ce43da891c338d2d319a593a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 05:34:56 +0300 Subject: [PATCH 216/290] Removes extra space in `@setObjectField' Issue: #32 --- include/Core.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Core.ll b/include/Core.ll index cd065fa..02627c2 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -261,7 +261,7 @@ define %TObject* @getObjectField(%TObject* %object, i32 %index) { } define %TObject** @setObjectField(%TObject* %object, i32 %index, %TObject* %value) { - %fieldPtr = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) + %fieldPtr = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) store %TObject* %value, %TObject** %fieldPtr ret %TObject** %fieldPtr } From c943a7159434ec1320a79ff611cefe47c577d702 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 05:39:07 +0300 Subject: [PATCH 217/290] Adds function to_string to format names of llvm values Issue: #32 --- src/MethodCompiler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index a1ab815..cbef637 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -47,6 +47,14 @@ using namespace llvm; +template +std::string to_string(const T& x) { + // FIXME there is the same function in C++11 + std::ostringstream ss; + ss << x; + return ss.str(); +} + Value* MethodCompiler::TJITContext::getLiteral(uint32_t index) { Module* jitModule = JITRuntime::Instance()->getModule(); From 8e15d58f5789564de7b3ee83a0c9a5a47f1d7d4b Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 05:44:13 +0300 Subject: [PATCH 218/290] Rewrites doPushArgument with `@getTemporaryFromContext' Issue: #32 --- include/Core.ll | 6 ++++++ include/jit.h | 2 ++ src/MethodCompiler.cpp | 17 ++++++----------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/Core.ll b/include/Core.ll index 02627c2..3f14586 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -291,6 +291,12 @@ define %TObject* @getTempsFromContext(%TContext* %context) { ret %TObject* %tempsObj } +define %TObject* @getTemporaryFromContext(%TContext* %context, i32 %index) { + %temps = call %TObject* @getTempsFromContext(%TContext* %context) + %temporary = call %TObject* @getObjectField(%TObject* %temps, i32 %index) + ret %TObject* %temporary +} + define void @dummy() gc "shadow-stack" { ; enabling shadow stack init on this module ret void diff --git a/include/jit.h b/include/jit.h index 2d952ca..05b4c7f 100644 --- a/include/jit.h +++ b/include/jit.h @@ -146,6 +146,7 @@ struct TBaseFunctions { llvm::Function* setObjectField; llvm::Function* getSlotSize; llvm::Function* getLiteral; + llvm::Function* getTemporary; void initializeFromModule(llvm::Module* module) { isSmallInteger = module->getFunction("isSmallInteger"); @@ -160,6 +161,7 @@ struct TBaseFunctions { setObjectField = module->getFunction("setObjectField"); getSlotSize = module->getFunction("getSlotSize"); getLiteral = module->getFunction("getLiteralFromContext"); + getTemporary = module->getFunction("getTemporaryFromContext"); } }; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index cbef637..fb70d28 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -672,17 +672,12 @@ void MethodCompiler::doPushArgument(TJITContext& jit) void MethodCompiler::doPushTemporary(TJITContext& jit) { const uint8_t index = jit.currentNode->getInstruction().getArgument(); - - Function* const getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); - Function* const getObjectField = m_JITModule->getFunction("getObjectField"); - - Value* const context = jit.getCurrentContext(); - Value* const temps = jit.builder->CreateCall(getTempsFromContext, context); - Value* const temporary = jit.builder->CreateCall2(getObjectField, temps, jit.builder->getInt32(index)); - - std::ostringstream ss; - ss << "temp" << index << "."; - temporary->setName(ss.str()); + Value* const temporary = jit.builder->CreateCall2( + m_baseFunctions.getTemporary, + jit.getCurrentContext(), + jit.builder->getInt32(index) + ); + temporary->setName(std::string("temp") + to_string(index) + "."); Value* const holder = protectProducerNode(jit, jit.currentNode, temporary); setNodeValue(jit, jit.currentNode, holder); From eca6bdf6d3d4f63c0906e4a1fe0865a2ad1cc15a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 05:49:38 +0300 Subject: [PATCH 219/290] Rewrites doAssignTemporary with `@setTemporaryInContext' Issue: #32 --- include/Core.ll | 6 ++++++ include/jit.h | 2 ++ src/MethodCompiler.cpp | 13 +++++++------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/Core.ll b/include/Core.ll index 3f14586..9347016 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -297,6 +297,12 @@ define %TObject* @getTemporaryFromContext(%TContext* %context, i32 %index) { ret %TObject* %temporary } +define void @setTemporaryInContext(%TContext* %context, i32 %index, %TObject* %value) { + %temps = call %TObject* @getTempsFromContext(%TContext* %context) + call %TObject** @setObjectField(%TObject* %temps, i32 %index, %TObject* %value) + ret void +} + define void @dummy() gc "shadow-stack" { ; enabling shadow stack init on this module ret void diff --git a/include/jit.h b/include/jit.h index 05b4c7f..b73a2c5 100644 --- a/include/jit.h +++ b/include/jit.h @@ -147,6 +147,7 @@ struct TBaseFunctions { llvm::Function* getSlotSize; llvm::Function* getLiteral; llvm::Function* getTemporary; + llvm::Function* setTemporary; void initializeFromModule(llvm::Module* module) { isSmallInteger = module->getFunction("isSmallInteger"); @@ -162,6 +163,7 @@ struct TBaseFunctions { getSlotSize = module->getFunction("getSlotSize"); getLiteral = module->getFunction("getLiteralFromContext"); getTemporary = module->getFunction("getTemporaryFromContext"); + setTemporary = module->getFunction("setTemporaryInContext"); } }; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index fb70d28..93120ce 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -831,13 +831,14 @@ void MethodCompiler::doPushBlock(TJITContext& jit) void MethodCompiler::doAssignTemporary(TJITContext& jit) { const uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* const value = getArgument(jit); //jit.lastValue(); - IRBuilder<>& builder = * jit.builder; + Value* const value = getArgument(jit); - Function* getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); - Value* const context = jit.getCurrentContext(); - Value* const temps = builder.CreateCall(getTempsFromContext, context); - builder.CreateCall3(m_baseFunctions.setObjectField, temps, builder.getInt32(index), value); + jit.builder->CreateCall3( + m_baseFunctions.setTemporary, + jit.getCurrentContext(), + jit.builder->getInt32(index), + value + ); } void MethodCompiler::doAssignInstance(TJITContext& jit) From 5f3eb77101cd238e5ea58161f52682a836f52265 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 05:53:56 +0300 Subject: [PATCH 220/290] Rewrites TJITContext::getLiteral using cached `@getLiteralFromContext' Issue: #32 --- src/MethodCompiler.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 93120ce..41bc1c2 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -57,16 +57,12 @@ std::string to_string(const T& x) { Value* MethodCompiler::TJITContext::getLiteral(uint32_t index) { - Module* jitModule = JITRuntime::Instance()->getModule(); - Function* getLiteralFromContext = jitModule->getFunction("getLiteralFromContext"); - - Value* context = getCurrentContext(); - CallInst* literal = builder->CreateCall2(getLiteralFromContext, context, builder->getInt32(index)); - - std::ostringstream ss; - ss << "lit" << index << "."; - literal->setName(ss.str()); - + Value* const literal = builder->CreateCall2( + compiler->m_baseFunctions.getLiteral, + getCurrentContext(), + builder->getInt32(index) + ); + literal->setName(std::string("lit") + to_string(index) + "."); return literal; } From e7ff4e4e5dcbca68899bbe87e2ab5327252eaf7c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 05:55:16 +0300 Subject: [PATCH 221/290] Rewrites doPushLiteral using TJITContext::getLiteral Issue: #32 --- src/MethodCompiler.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 41bc1c2..138bb09 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -696,14 +696,7 @@ void MethodCompiler::doPushLiteral(TJITContext& jit) ss << "const" << result << "."; result->setName(ss.str()); } else { - Function* const getLiteralFromContext = m_JITModule->getFunction("getLiteralFromContext"); - Value* const context = jit.getCurrentContext(); - Value* const literalValue = jit.builder->CreateCall2(getLiteralFromContext, context, jit.builder->getInt32(index)); - - std::ostringstream ss; - ss << "lit" << (uint32_t) index << "."; - literalValue->setName(ss.str()); - + Value* const literalValue = jit.getLiteral(index); result = protectProducerNode(jit, jit.currentNode, literalValue); } From 45e9a97238b12dd5eee6ad2a3830cff248153517 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 06:01:43 +0300 Subject: [PATCH 222/290] Rewrites doPushArgument using cached `@getArgFromContext' Issue: #32 --- include/jit.h | 2 ++ src/MethodCompiler.cpp | 13 ++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/jit.h b/include/jit.h index b73a2c5..7a915e0 100644 --- a/include/jit.h +++ b/include/jit.h @@ -146,6 +146,7 @@ struct TBaseFunctions { llvm::Function* setObjectField; llvm::Function* getSlotSize; llvm::Function* getLiteral; + llvm::Function* getArgument; llvm::Function* getTemporary; llvm::Function* setTemporary; @@ -162,6 +163,7 @@ struct TBaseFunctions { setObjectField = module->getFunction("setObjectField"); getSlotSize = module->getFunction("getSlotSize"); getLiteral = module->getFunction("getLiteralFromContext"); + getArgument = module->getFunction("getArgFromContext"); getTemporary = module->getFunction("getTemporaryFromContext"); setTemporary = module->getFunction("setTemporaryInContext"); } diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 138bb09..02257db 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -653,13 +653,12 @@ void MethodCompiler::doPushArgument(TJITContext& jit) return; } - Function* const getArgFromContext = m_JITModule->getFunction("getArgFromContext"); - Value* const context = jit.getCurrentContext(); - Value* const argument = jit.builder->CreateCall2(getArgFromContext, context, jit.builder->getInt32(index)); - - std::ostringstream ss; - ss << "arg" << index << "."; - argument->setName(ss.str()); + Value* const argument = jit.builder->CreateCall2( + m_baseFunctions.getArgument, + jit.getCurrentContext(), + jit.builder->getInt32(index) + ); + argument->setName(std::string("arg") + to_string(index) + "."); Value* const holder = protectProducerNode(jit, jit.currentNode, argument); setNodeValue(jit, jit.currentNode, holder); From 2c4b48d244b2cdfb3daa39c004554feaf785300b Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 06:07:19 +0300 Subject: [PATCH 223/290] Rewrites doPushInstance using cached `@getInstanceFromContext' Issue: #32 --- include/Core.ll | 6 ++++++ include/jit.h | 2 ++ src/MethodCompiler.cpp | 18 +++++++----------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/Core.ll b/include/Core.ll index 9347016..1edb159 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -303,6 +303,12 @@ define void @setTemporaryInContext(%TContext* %context, i32 %index, %TObject* %v ret void } +define %TObject* @getInstanceFromContext(%TContext* %context, i32 %index) { + %self = call %TObject* @getArgFromContext(%TContext* %context, i32 0) + %instance = call %TObject* @getObjectField(%TObject* %self, i32 %index) + ret %TObject* %instance +} + define void @dummy() gc "shadow-stack" { ; enabling shadow stack init on this module ret void diff --git a/include/jit.h b/include/jit.h index 7a915e0..b24223f 100644 --- a/include/jit.h +++ b/include/jit.h @@ -147,6 +147,7 @@ struct TBaseFunctions { llvm::Function* getSlotSize; llvm::Function* getLiteral; llvm::Function* getArgument; + llvm::Function* getInstance; llvm::Function* getTemporary; llvm::Function* setTemporary; @@ -164,6 +165,7 @@ struct TBaseFunctions { getSlotSize = module->getFunction("getSlotSize"); getLiteral = module->getFunction("getLiteralFromContext"); getArgument = module->getFunction("getArgFromContext"); + getInstance = module->getFunction("getInstanceFromContext"); getTemporary = module->getFunction("getTemporaryFromContext"); setTemporary = module->getFunction("setTemporaryInContext"); } diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 02257db..b0e742d 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -628,18 +628,14 @@ void MethodCompiler::writeLandingPad(TJITContext& jit) void MethodCompiler::doPushInstance(TJITContext& jit) { const uint8_t index = jit.currentNode->getInstruction().getArgument(); - Function* const getObjectField = m_JITModule->getFunction("getObjectField"); - - // Self is interpreted as object array. - // Array elements are instance variables. - Value* const self = jit.getSelf(); - Value* const field = jit.builder->CreateCall2(getObjectField, self, jit.builder->getInt32(index)); - - std::ostringstream ss; - ss << "field" << index << "."; - field->setName(ss.str()); + Value* const instance = jit.builder->CreateCall2( + m_baseFunctions.getInstance, + jit.getCurrentContext(), + jit.builder->getInt32(index) + ); + instance->setName(std::string("instance") + to_string(index) + "."); - Value* const holder = protectProducerNode(jit, jit.currentNode, field); + Value* const holder = protectProducerNode(jit, jit.currentNode, instance); setNodeValue(jit, jit.currentNode, holder); } From d8effb6893bdaa97d365f853d76eeb6602796b6f Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 06:09:47 +0300 Subject: [PATCH 224/290] Rewrites primitive::arrayAtPut using cached `@getObjectFieldPtr' Issue: #32 --- include/jit.h | 2 ++ src/MethodCompiler.cpp | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/jit.h b/include/jit.h index b24223f..86e1388 100644 --- a/include/jit.h +++ b/include/jit.h @@ -142,6 +142,7 @@ struct TBaseFunctions { llvm::Function* getObjectClass; llvm::Function* setObjectClass; llvm::Function* getObjectFields; + llvm::Function* getObjectFieldPtr; llvm::Function* getObjectField; llvm::Function* setObjectField; llvm::Function* getSlotSize; @@ -160,6 +161,7 @@ struct TBaseFunctions { getObjectClass = module->getFunction("getObjectClass"); setObjectClass = module->getFunction("setObjectClass"); getObjectFields = module->getFunction("getObjectFields"); + getObjectFieldPtr= module->getFunction("getObjectFieldPtr"); getObjectField = module->getFunction("getObjectField"); setObjectField = module->getFunction("setObjectField"); getSlotSize = module->getFunction("getSlotSize"); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index b0e742d..4198db4 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -1740,8 +1740,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, jit.builder->SetInsertPoint(indexChecked); if (opcode == primitive::arrayAtPut) { - Function* const getObjectFieldPtr = m_JITModule->getFunction("getObjectFieldPtr"); - Value* const fieldPointer = jit.builder->CreateCall2(getObjectFieldPtr, arrayObject, actualIndex); + Value* const fieldPointer = jit.builder->CreateCall2(m_baseFunctions.getObjectFieldPtr, arrayObject, actualIndex); jit.builder->CreateCall2(m_runtimeAPI.checkRoot, valueObejct, fieldPointer); jit.builder->CreateStore(valueObejct, fieldPointer); From ab172244525410f5f11d13460d6875fd073a64e3 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 06:12:14 +0300 Subject: [PATCH 225/290] Fixes using of `@getTempsFromContext' Issue: #32 --- include/jit.h | 2 ++ src/MethodCompiler.cpp | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/jit.h b/include/jit.h index 86e1388..6f19a20 100644 --- a/include/jit.h +++ b/include/jit.h @@ -149,6 +149,7 @@ struct TBaseFunctions { llvm::Function* getLiteral; llvm::Function* getArgument; llvm::Function* getInstance; + llvm::Function* getTemps; llvm::Function* getTemporary; llvm::Function* setTemporary; @@ -168,6 +169,7 @@ struct TBaseFunctions { getLiteral = module->getFunction("getLiteralFromContext"); getArgument = module->getFunction("getArgFromContext"); getInstance = module->getFunction("getInstanceFromContext"); + getTemps = module->getFunction("getTempsFromContext"); getTemporary = module->getFunction("getTemporaryFromContext"); setTemporary = module->getFunction("setTemporaryInContext"); } diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 4198db4..a3abefa 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -1659,9 +1659,7 @@ void MethodCompiler::compilePrimitive(TJITContext& jit, const uint32_t argCount = jit.currentNode->getInstruction().getArgument() - 1; Value* const blockAsContext = jit.builder->CreateBitCast(block, m_baseTypes.context->getPointerTo()); - Function* const getTempsFromContext = m_JITModule->getFunction("getTempsFromContext"); - Value* const blockTemps = jit.builder->CreateCall(getTempsFromContext, blockAsContext); - + Value* const blockTemps = jit.builder->CreateCall(m_baseFunctions.getTemps, blockAsContext); Value* const tempsSize = jit.builder->CreateCall(m_baseFunctions.getObjectSize, blockTemps, "tempsSize."); Value* const argumentLocationPtr = jit.builder->CreateStructGEP(block, 1); From d2be22f1f436e4cf5fc7e636d467dafcfd214920 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 06:26:37 +0300 Subject: [PATCH 226/290] Rewrites doAssignInstance using cached `@getObjectFieldPtr' Issue: #32 --- src/MethodCompiler.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index a3abefa..4660543 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -828,12 +828,11 @@ void MethodCompiler::doAssignTemporary(TJITContext& jit) void MethodCompiler::doAssignInstance(TJITContext& jit) { const uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* const value = getArgument(jit); // jit.lastValue(); + Value* const value = getArgument(jit); IRBuilder<>& builder = * jit.builder; - Function* const getObjectFieldPtr = m_JITModule->getFunction("getObjectFieldPtr"); - Value* const self = jit.getSelf(); - Value* const fieldPointer = builder.CreateCall2(getObjectFieldPtr, self, builder.getInt32(index)); + Value* const self = jit.getSelf(); + Value* const fieldPointer = builder.CreateCall2(m_baseFunctions.getObjectFieldPtr, self, builder.getInt32(index)); builder.CreateCall2(m_runtimeAPI.checkRoot, value, fieldPointer); builder.CreateStore(value, fieldPointer); From f6ebc6be35607880b2dbb0d01551d8bfc55830ec Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Apr 2015 22:55:57 +0300 Subject: [PATCH 227/290] Adds test to check whether all incomings of Phi are unique Issue: #32 --- tests/helpers/ControlGraph.h | 26 ++++++++++++++++++++++++++ tests/stack_semantics.cpp | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index f60e659..54efee6 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -336,6 +336,28 @@ class H_BranchJumpsOnCorrectNode: public st::NodeVisitor } }; +class H_NonUniqueIncomingsOfPhi: public st::PlainNodeVisitor +{ +public: + H_NonUniqueIncomingsOfPhi(st::ControlGraph* graph) : st::PlainNodeVisitor(graph) {} + virtual bool visitNode(st::ControlNode& node) { + struct CompareIncoming { + static bool cmp(const st::PhiNode::TIncoming& left, const st::PhiNode::TIncoming& right) { + return left.node == right.node; + } + }; + + if (st::PhiNode* phi = node.cast()) { + st::PhiNode::TIncomingList incomingList = phi->getIncomingList(); + st::PhiNode::TIncomingList::iterator uniqueEnd = std::unique(incomingList.begin(), incomingList.end(), CompareIncoming::cmp); + std::size_t uniqueSize = std::distance(incomingList.begin(), uniqueEnd); + EXPECT_EQ(uniqueSize, phi->getIncomingList().size()) + << "The incomings of phi must differ between each other"; + } + return true; + } +}; + void H_CheckCFGCorrect(st::ControlGraph* graph) { { @@ -366,6 +388,10 @@ void H_CheckCFGCorrect(st::ControlGraph* graph) // H_BranchJumpsOnCorrectNode visitor(graph); // visitor.run(); } + { + H_NonUniqueIncomingsOfPhi visitor(graph); + visitor.run(); + } } #endif diff --git a/tests/stack_semantics.cpp b/tests/stack_semantics.cpp index 281cc08..ed3910d 100644 --- a/tests/stack_semantics.cpp +++ b/tests/stack_semantics.cpp @@ -1,4 +1,5 @@ #include "patterns/DecodeBytecode.h" +#include "helpers/ControlGraph.h" static const uint8_t bytecode[] = { 81, // 0000 PushConstant 1 @@ -24,6 +25,12 @@ INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, ::testing::Values( std::tr1::make_tuple(std::string("Bytecode"), std::string(reinterpret_cast(bytecode), sizeof(bytecode))) ) ); +TEST_P(P_DecodeBytecode, CorrectPhi) +{ + H_NonUniqueIncomingsOfPhi visitor(m_cfg); + visitor.run(); +} + TEST_P(P_DecodeBytecode, StackSemanticsTemps) { class TempsLoadInCorrectBB: public st::NodeVisitor From 9b3c65589bce44e156a84e7472c6f559f6d2d27a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 26 Feb 2015 19:38:34 +0300 Subject: [PATCH 228/290] Rewrites doAssignInstance with `@setInstanceInContext' Issue: #32 --- include/Core.ll | 8 ++++++++ include/jit.h | 2 ++ src/MethodCompiler.cpp | 14 ++++++-------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/include/Core.ll b/include/Core.ll index 1edb159..3d399b6 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -309,6 +309,14 @@ define %TObject* @getInstanceFromContext(%TContext* %context, i32 %index) { ret %TObject* %instance } +define void @setInstanceInContext(%TContext* %context, i32 %index, %TObject* %value) { + %self = call %TObject* @getArgFromContext(%TContext* %context, i32 0) + %instancePtr = call %TObject** @getObjectFieldPtr(%TObject* %self, i32 %index) + call void @checkRoot(%TObject* %value, %TObject** %instancePtr) + store %TObject* %value, %TObject** %instancePtr + ret void +} + define void @dummy() gc "shadow-stack" { ; enabling shadow stack init on this module ret void diff --git a/include/jit.h b/include/jit.h index 6f19a20..a6cec10 100644 --- a/include/jit.h +++ b/include/jit.h @@ -149,6 +149,7 @@ struct TBaseFunctions { llvm::Function* getLiteral; llvm::Function* getArgument; llvm::Function* getInstance; + llvm::Function* setInstance; llvm::Function* getTemps; llvm::Function* getTemporary; llvm::Function* setTemporary; @@ -169,6 +170,7 @@ struct TBaseFunctions { getLiteral = module->getFunction("getLiteralFromContext"); getArgument = module->getFunction("getArgFromContext"); getInstance = module->getFunction("getInstanceFromContext"); + setInstance = module->getFunction("setInstanceInContext"); getTemps = module->getFunction("getTempsFromContext"); getTemporary = module->getFunction("getTemporaryFromContext"); setTemporary = module->getFunction("setTemporaryInContext"); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 4660543..25ee072 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -828,14 +828,12 @@ void MethodCompiler::doAssignTemporary(TJITContext& jit) void MethodCompiler::doAssignInstance(TJITContext& jit) { const uint8_t index = jit.currentNode->getInstruction().getArgument(); - Value* const value = getArgument(jit); - IRBuilder<>& builder = * jit.builder; - - Value* const self = jit.getSelf(); - Value* const fieldPointer = builder.CreateCall2(m_baseFunctions.getObjectFieldPtr, self, builder.getInt32(index)); - - builder.CreateCall2(m_runtimeAPI.checkRoot, value, fieldPointer); - builder.CreateStore(value, fieldPointer); + jit.builder->CreateCall3( + m_baseFunctions.setInstance, + jit.getCurrentContext(), + jit.builder->getInt32(index), + getArgument(jit) + ); } void MethodCompiler::doMarkArguments(TJITContext& jit) From 20bde065747090621f0aa6ab195cb9108b8dfcea Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 12 Apr 2015 18:34:14 +0600 Subject: [PATCH 229/290] Disables stack underflow test Issue: #32 --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4cb7eab..8036f37 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,5 +18,5 @@ cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;-pthread") cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;-pthread") cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;-pthread") -cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi;-pthread") +# TODO cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi;-pthread") cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set;-pthread") From f46f391dd66143a8677db5e9ad636284bb5dc8ae Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 7 Mar 2015 00:42:05 +0300 Subject: [PATCH 230/290] Forces IR functions to be inlined Issue: #32 --- include/Core.ll | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/include/Core.ll b/include/Core.ll index 3d399b6..44f49f0 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -153,7 +153,7 @@ ;;;;;;;;;;;;;;;;;;;; functions ;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -define i1 @isSmallInteger(%TObject* %value) { +define i1 @isSmallInteger(%TObject* %value) alwaysinline { ; return reinterpret_cast(value) & 1; %int = ptrtoint %TObject* %value to i32 @@ -161,7 +161,7 @@ define i1 @isSmallInteger(%TObject* %value) { ret i1 %result } -define i32 @getIntegerValue(%TObject* %value) { +define i32 @getIntegerValue(%TObject* %value) alwaysinline { ; return (int32_t) (value >> 1) %int = ptrtoint %TObject* %value to i32 @@ -169,7 +169,7 @@ define i32 @getIntegerValue(%TObject* %value) { ret i32 %result } -define %TObject* @newInteger(i32 %value) { +define %TObject* @newInteger(i32 %value) alwaysinline { ; return reinterpret_cast( (value << 1) | 1 ); %shled = shl i32 %value, 1 @@ -178,7 +178,7 @@ define %TObject* @newInteger(i32 %value) { ret %TObject* %result } -define i32 @getSlotSize(i32 %fieldsCount) { +define i32 @getSlotSize(i32 %fieldsCount) alwaysinline { ;sizeof(TObject) + fieldsCount * sizeof(TObject*) %fieldsSize = mul i32 4, %fieldsCount @@ -188,21 +188,21 @@ define i32 @getSlotSize(i32 %fieldsCount) { } -define i32 @getObjectSize(%TObject* %this) { +define i32 @getObjectSize(%TObject* %this) alwaysinline { %1 = getelementptr %TObject* %this, i32 0, i32 0, i32 0 %data = load i32* %1 %result = lshr i32 %data, 2 ret i32 %result } -define %TObject* @setObjectSize(%TObject* %this, i32 %size) { +define %TObject* @setObjectSize(%TObject* %this, i32 %size) alwaysinline { %addr = getelementptr %TObject* %this, i32 0, i32 0, i32 0 %ssize = shl i32 %size, 2 store i32 %ssize, i32* %addr ret %TObject* %this } -define i1 @isObjectRelocated(%TObject* %this) { +define i1 @isObjectRelocated(%TObject* %this) alwaysinline { %1 = getelementptr %TObject* %this, i32 0, i32 0, i32 0 %data = load i32* %1 %field = and i32 %data, 1 @@ -210,7 +210,7 @@ define i1 @isObjectRelocated(%TObject* %this) { ret i1 %result } -define i1 @isObjectBinary(%TObject* %this) { +define i1 @isObjectBinary(%TObject* %this) alwaysinline { %1 = getelementptr %TObject* %this, i32 0, i32 0, i32 0 %data = load i32* %1 %field = and i32 %data, 2 @@ -218,14 +218,14 @@ define i1 @isObjectBinary(%TObject* %this) { ret i1 %result } -define %TClass** @getObjectClassPtr(%TObject* %this) { +define %TClass** @getObjectClassPtr(%TObject* %this) alwaysinline { %pclass = getelementptr inbounds %TObject* %this, i32 0, i32 1 ret %TClass** %pclass } @SmallInt = external global %TClass -define %TClass* @getObjectClass(%TObject* %this) { +define %TClass* @getObjectClass(%TObject* %this) alwaysinline { ; TODO SmallInt case %test = call i1 @isSmallInteger(%TObject* %this) br i1 %test, label %is_smallint, label %is_object @@ -237,36 +237,36 @@ is_object: ret %TClass* %class } -define %TObject* @setObjectClass(%TObject* %this, %TClass* %class) { +define %TObject* @setObjectClass(%TObject* %this, %TClass* %class) alwaysinline { %addr = call %TClass** @getObjectClassPtr(%TObject* %this) store %TClass* %class, %TClass** %addr ret %TObject* %this } -define %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) { +define %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) alwaysinline { %fields = getelementptr inbounds %TObject* %object, i32 0, i32 2 %fieldPtr = getelementptr inbounds [0 x %TObject*]* %fields, i32 0, i32 %index ret %TObject** %fieldPtr } -define %TObject** @getObjectFields(%TObject* %this) { +define %TObject** @getObjectFields(%TObject* %this) alwaysinline { %fieldsPtr = call %TObject** @getObjectFieldPtr(%TObject* %this, i32 0) ret %TObject** %fieldsPtr } -define %TObject* @getObjectField(%TObject* %object, i32 %index) { +define %TObject* @getObjectField(%TObject* %object, i32 %index) alwaysinline { %fieldPtr = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) %result = load %TObject** %fieldPtr ret %TObject* %result } -define %TObject** @setObjectField(%TObject* %object, i32 %index, %TObject* %value) { +define %TObject** @setObjectField(%TObject* %object, i32 %index, %TObject* %value) alwaysinline { %fieldPtr = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) store %TObject* %value, %TObject** %fieldPtr ret %TObject** %fieldPtr } -define %TObject* @getArgFromContext(%TContext* %context, i32 %index) { +define %TObject* @getArgFromContext(%TContext* %context, i32 %index) alwaysinline { %argsPtr = getelementptr inbounds %TContext* %context, i32 0, i32 2 %args = load %TObjectArray** %argsPtr %argsObj = bitcast %TObjectArray* %args to %TObject* @@ -274,7 +274,7 @@ define %TObject* @getArgFromContext(%TContext* %context, i32 %index) { ret %TObject* %arg } -define %TObject* @getLiteralFromContext(%TContext* %context, i32 %index) { +define %TObject* @getLiteralFromContext(%TContext* %context, i32 %index) alwaysinline { %methodPtr = getelementptr inbounds %TContext* %context, i32 0, i32 1 %method = load %TMethod** %methodPtr %literalsPtr = getelementptr inbounds %TMethod* %method, i32 0, i32 3 @@ -284,32 +284,32 @@ define %TObject* @getLiteralFromContext(%TContext* %context, i32 %index) { ret %TObject* %literal } -define %TObject* @getTempsFromContext(%TContext* %context) { +define %TObject* @getTempsFromContext(%TContext* %context) alwaysinline { %tempsPtr = getelementptr inbounds %TContext* %context, i32 0, i32 3 %temps = load %TObjectArray** %tempsPtr %tempsObj = bitcast %TObjectArray* %temps to %TObject* ret %TObject* %tempsObj } -define %TObject* @getTemporaryFromContext(%TContext* %context, i32 %index) { +define %TObject* @getTemporaryFromContext(%TContext* %context, i32 %index) alwaysinline { %temps = call %TObject* @getTempsFromContext(%TContext* %context) %temporary = call %TObject* @getObjectField(%TObject* %temps, i32 %index) ret %TObject* %temporary } -define void @setTemporaryInContext(%TContext* %context, i32 %index, %TObject* %value) { +define void @setTemporaryInContext(%TContext* %context, i32 %index, %TObject* %value) alwaysinline { %temps = call %TObject* @getTempsFromContext(%TContext* %context) call %TObject** @setObjectField(%TObject* %temps, i32 %index, %TObject* %value) ret void } -define %TObject* @getInstanceFromContext(%TContext* %context, i32 %index) { +define %TObject* @getInstanceFromContext(%TContext* %context, i32 %index) alwaysinline { %self = call %TObject* @getArgFromContext(%TContext* %context, i32 0) %instance = call %TObject* @getObjectField(%TObject* %self, i32 %index) ret %TObject* %instance } -define void @setInstanceInContext(%TContext* %context, i32 %index, %TObject* %value) { +define void @setInstanceInContext(%TContext* %context, i32 %index, %TObject* %value) alwaysinline { %self = call %TObject* @getArgFromContext(%TContext* %context, i32 0) %instancePtr = call %TObject** @getObjectFieldPtr(%TObject* %self, i32 %index) call void @checkRoot(%TObject* %value, %TObject** %instancePtr) From 60cf64af94df1b1adb9cc32f86e1e5af64718aeb Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 12 Apr 2015 19:59:04 +0600 Subject: [PATCH 231/290] Fixes gtest include path (broken during merge) --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2ad79b7..abf47db 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,7 @@ macro(cxx_test pretty_name bin_name sources libs) add_dependencies(check ${bin_name}) endmacro() -include_directories(${CMAKE_SOURCE_DIR}/include ${GTEST_INCLUDE_DIR}/include) +include_directories(${CMAKE_SOURCE_DIR}/include ${GTEST_INCLUDE_DIR}) cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;-pthread") cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;-pthread") From 5dc9e4d4e5051a704f097453ac40a9041df4e2f9 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 12 Apr 2015 17:48:41 +0300 Subject: [PATCH 232/290] Fixes gtest lib to make our tests work --- cmake/GTest.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/GTest.cmake b/cmake/GTest.cmake index fd009c0..16f7127 100644 --- a/cmake/GTest.cmake +++ b/cmake/GTest.cmake @@ -28,7 +28,7 @@ function(add_gtest_as_external) PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gtest INSTALL_COMMAND "" CMAKE_ARGS - -Dgtest_disable_pthreads=On + -Dgtest_disable_pthreads=Off -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} # We don't want to check compiler by subproject again -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} @@ -39,7 +39,7 @@ function(add_gtest_as_external) -DCMAKE_C_COMPILER_ID_RUN=TRUE -DCMAKE_C_COMPILER_WORKS=TRUE -DCMAKE_C_COMPILER_FORCED=TRUE - -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}\ -Wno-missing-field-initializers ) ExternalProject_Get_Property(gtest-external source_dir binary_dir) set(GTEST_INCLUDE_DIR "${source_dir}/include" CACHE PATH "Path to directory." FORCE) From b553bd68015c935444d8e3f01fdee100f49dea80 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Mon, 13 Apr 2015 16:39:51 +0300 Subject: [PATCH 233/290] Fixes comparison of integers of different signs [-Wsign-compare] --- tests/abab_problem.cpp | 8 ++++---- tests/helpers/ControlGraph.h | 34 ++++++++++++++++------------------ 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/tests/abab_problem.cpp b/tests/abab_problem.cpp index 0ffe767..791330a 100644 --- a/tests/abab_problem.cpp +++ b/tests/abab_problem.cpp @@ -40,7 +40,7 @@ void checkSendBinaryArg(st::InstructionNode* inst, int idx) { case 0: { // The first arg is a phi containing phis >< - ASSERT_EQ(2, phiArg->getInEdges().size()); + ASSERT_EQ(2u, phiArg->getInEdges().size()); st::ControlNode* phi1 = * phiArg->getInEdges().begin(); st::ControlNode* phi2 = * ++ phiArg->getInEdges().begin(); phisToCheck.push_back(phi1->cast()); @@ -52,7 +52,7 @@ void checkSendBinaryArg(st::InstructionNode* inst, int idx) default: FAIL() << "idx should be 0 or 1"; } - ASSERT_GT(phisToCheck.size(), 0); + ASSERT_GT(phisToCheck.size(), 0u); { for(std::size_t phiIdx = 0; phiIdx < phisToCheck.size(); phiIdx++) { @@ -83,8 +83,8 @@ TEST_P(P_DecodeBytecode, ABAB) { sendBinaryFound = true; - EXPECT_EQ( 4, inst->getInEdges().size()); // 2 branches + 2 phis - EXPECT_EQ( 2, inst->getArgumentsCount() ); + EXPECT_EQ(4u, inst->getInEdges().size()); // 2 branches + 2 phis + EXPECT_EQ(2u, inst->getArgumentsCount()); EXPECT_NE(inst->getArgument(0), inst->getArgument(1)); { diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 54efee6..0a4e699 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -34,9 +34,9 @@ class H_LastInstIsTerminator: public st::BasicBlockVisitor } { SCOPED_TRACE("There must be no terminators but the last one"); - int terminatorsCount = 0; + uint32_t terminatorsCount = 0; st::BasicBlock::iterator iInstruction = BB.begin(); - st::BasicBlock::iterator iEnd = BB.end()-1; + st::BasicBlock::iterator iEnd = BB.end()-1; // except the last inst while (iInstruction != iEnd) { bool isTerminator = (*iInstruction).isTerminator(); @@ -45,7 +45,7 @@ class H_LastInstIsTerminator: public st::BasicBlockVisitor EXPECT_FALSE(isTerminator); ++iInstruction; } - EXPECT_EQ(0, terminatorsCount); + EXPECT_EQ(0u, terminatorsCount); } return true; @@ -76,7 +76,7 @@ class H_AreBBsLinked: public st::NodeVisitor virtual bool visitDomain(st::ControlDomain& domain) { st::BasicBlock* currentBB = domain.getBasicBlock(); if (currentBB->getOffset() != 0) { - EXPECT_GT(currentBB->getReferers().size(), 0) + EXPECT_GT(currentBB->getReferers().size(), 0u) << "All BB but the 1st must have referrers. " << "BB offset: " << currentBB->getOffset(); } @@ -100,7 +100,7 @@ class H_AreBBsLinked: public st::NodeVisitor st::TSmalltalkInstruction branch = inst->getInstruction(); const st::TNodeSet& outEdges = node.getOutEdges(); if (branch.getArgument() == special::branchIfTrue || branch.getArgument() == special::branchIfFalse) { - EXPECT_EQ(outEdges.size(), 2); + EXPECT_EQ(2u, outEdges.size()); if (outEdges.size() == 2) { st::TNodeSet::const_iterator edgeIter = outEdges.begin(); st::ControlNode* target1 = *(edgeIter++); @@ -127,7 +127,7 @@ class H_AreBBsLinked: public st::NodeVisitor } } if (branch.getArgument() == special::branch) { - EXPECT_EQ(outEdges.size(), 1); + EXPECT_EQ(1u, outEdges.size()); if (outEdges.size() == 1) { st::ControlNode* target = *(outEdges.begin()); st::ControlDomain* targetDomain = target->getDomain(); @@ -160,15 +160,15 @@ class H_CorrectNumOfEdges: public st::PlainNodeVisitor case opcode::pushLiteral: case opcode::pushConstant: case opcode::pushBlock: - EXPECT_EQ( inst->getArgumentsCount(), 0); + EXPECT_EQ(0u, inst->getArgumentsCount()); break; case opcode::sendUnary: case opcode::assignInstance: case opcode::assignTemporary: - EXPECT_EQ( inst->getArgumentsCount(), 1); + EXPECT_EQ(1u, inst->getArgumentsCount()); break; case opcode::sendBinary: - EXPECT_EQ( inst->getArgumentsCount(), 2); + EXPECT_EQ(2u, inst->getArgumentsCount()); break; case opcode::doSpecial: { switch (inst->getInstruction().getArgument()) { @@ -178,13 +178,11 @@ class H_CorrectNumOfEdges: public st::PlainNodeVisitor case special::branchIfTrue: case special::branchIfFalse: case special::duplicate: - EXPECT_EQ( inst->getArgumentsCount(), 1); + case special::sendToSuper: + EXPECT_EQ(1u, inst->getArgumentsCount()); break; case special::branch: - EXPECT_EQ( inst->getArgumentsCount(), 0); - break; - case special::sendToSuper: - EXPECT_EQ( inst->getArgumentsCount(), 1); + EXPECT_EQ(0u, inst->getArgumentsCount()); break; } } break; @@ -192,19 +190,19 @@ class H_CorrectNumOfEdges: public st::PlainNodeVisitor EXPECT_EQ(inst->getInstruction().getArgument(), inst->getArgumentsCount()); break; default: - EXPECT_GE( inst->getArgumentsCount(), 1); + EXPECT_GE(inst->getArgumentsCount(), 1u); break; } if (inst->getInstruction().isValueProvider() && inst->getInstruction().getOpcode() != opcode::pushBlock) { const st::TNodeSet& consumers = inst->getConsumers(); - EXPECT_GT(consumers.size(), 0); + EXPECT_GT(consumers.size(), 0u); } } if (st::PhiNode* phi = node.cast()) { const st::PhiNode::TIncomingList& incomingList = phi->getIncomingList(); const st::TNodeSet& outEdges = phi->getOutEdges(); - EXPECT_GT(incomingList.size(), 0) << "The phi must have at least 1 incoming edge"; - EXPECT_GE(outEdges.size(), 1) << "There must be a node using the given phi"; + EXPECT_GT(incomingList.size(), 0u) << "The phi must have at least 1 incoming edge"; + EXPECT_GE(outEdges.size(), 1u) << "There must be a node using the given phi"; } if (st::TauNode* tau = node.cast()) { EXPECT_TRUE(tau == NULL /* always fails */); // TODO From 244d5c8fdff54550cf626c6bd0c2c394b3f6eb45 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 17 Apr 2015 14:58:50 +0300 Subject: [PATCH 234/290] Fixes compiler error reports in travis --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 80182ff..2ed64c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,10 +33,9 @@ before_script: - cmake .. -DUSE_LLVM=$USE_LLVM -DBUILD_TESTS=On -DCMAKE_BUILD_TYPE=Release -DSITE="${TRAVIS_OS_NAME}@Travis" -DBUILDNAME="${TRAVIS_BRANCH}_${CXX}_LLVM_${USE_LLVM}" script: - - make ExperimentalBuild + - ctest -M Experimental -T Start -T Build -T Test -T Submit after_script: - cd build - - make ExperimentalTest - - make ExperimentalSubmit + - make check # If ctest built the project unsuccessfully, `make check` will report errors to Travis From c5a21f2387e4c540a4e084587248f32d7b5ae918 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Apr 2015 02:56:47 +0300 Subject: [PATCH 235/290] Adds Debug/Release build types --- .travis.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2ed64c0..81ed1be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,9 +20,15 @@ branches: - /^.*travis.*$/ env: - matrix: - - USE_LLVM=Off # if you rename On/Off, - - USE_LLVM=On # fix all scripts (e.g. install-dependencies.sh) + matrix: # fix all scripts (e.g. install-dependencies.sh) if you rename LLVM_USE[On/Off] + - USE_LLVM=Off + BUILD_TYPE=Debug + - USE_LLVM=Off + BUILD_TYPE=Release + - USE_LLVM=On + BUILD_TYPE=Debug + - USE_LLVM=On + BUILD_TYPE=Release before_install: - ./.travis/${TRAVIS_OS_NAME}/install-dependencies.sh @@ -30,7 +36,7 @@ before_install: before_script: - mkdir -p build - cd build - - cmake .. -DUSE_LLVM=$USE_LLVM -DBUILD_TESTS=On -DCMAKE_BUILD_TYPE=Release -DSITE="${TRAVIS_OS_NAME}@Travis" -DBUILDNAME="${TRAVIS_BRANCH}_${CXX}_LLVM_${USE_LLVM}" + - cmake .. -DUSE_LLVM=$USE_LLVM -DBUILD_TESTS=On -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DSITE="${TRAVIS_OS_NAME}@Travis" -DBUILDNAME="${TRAVIS_BRANCH}_${CXX}_LLVM_${USE_LLVM}_${BUILD_TYPE}" script: - ctest -M Experimental -T Start -T Build -T Test -T Submit From ee2bf70ee2268874c0e67233f974690cf1d51a5d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 17 Apr 2015 15:34:10 +0300 Subject: [PATCH 236/290] Outputs as much compiler errors as possible --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 81ed1be..f674040 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,6 +42,5 @@ script: - ctest -M Experimental -T Start -T Build -T Test -T Submit after_script: - - cd build - - make check # If ctest built the project unsuccessfully, `make check` will report errors to Travis + - make check --keep-going # If ctest built the project unsuccessfully, `make check` will report errors to Travis From 6292e95b55ffc8a611f0ba032c5cbe72703f8535 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 14 Apr 2015 22:26:22 +0600 Subject: [PATCH 237/290] Fixes warnings in MethodCompiler and ControlGraph --- src/ControlGraph.cpp | 23 ----------------------- src/MethodCompiler.cpp | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 2cd4bb4..2e97a94 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -97,7 +97,6 @@ class GraphConstructor : public InstructionVisitor { ControlGraph* const m_graph; ControlDomain* m_currentDomain; - bool m_skipStubInstructions; }; InstructionNode* GraphConstructor::createNode(const TSmalltalkInstruction& instruction) @@ -506,28 +505,6 @@ class GraphOptimizer : public PlainNodeVisitor { } } } - } else if (PhiNode* const phi = node.cast()) { - assert(incomings.size()); - - // We may remove the phi node if all of it's incoming entries map to the same node - /*const PhiNode::TIncomingList& incomings = phi->getIncomingList(); - const ControlNode* const testNode = incomings[0].node; - bool unique = true; - - for (std::size_t index = 1; index < incomings.size(); index++) { - if (incomings[index].node != testNode) { - unique = false; - break; - } - }*/ - - if (phi->getInEdges().size() == 1) { - if (traces_enabled) - std::printf("GraphOptimizer::visitNode : phi node %u has only one input and may be removed\n", phi->getIndex()); - - // FIXME Should be modified according to new phi linking rules - // m_nodesToRemove.push_back(phi); - } } return true; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index d4ace28..66e7f53 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -185,7 +185,7 @@ class Detector { } private: - st::PathVerifier& m_verifier; +// st::PathVerifier& m_verifier; bool m_detected; }; From c82b3c6a37b85ed43ace4bd7a6546a62d1c5aa24 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Tue, 14 Apr 2015 23:00:30 +0600 Subject: [PATCH 238/290] Replaces phi cast with direct node type check --- src/MethodCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 66e7f53..d724894 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -172,7 +172,7 @@ class Detector { return st::GraphWalker::vrSkipPath; } */ } - } else if (st::PhiNode* const phi = node->cast()) { + } else if (node->getNodeType() == st::ControlNode::ntPhi) { // Phi node may not cause gc, protects it's value separately // and do not have outgoing edges that we may traverse. From 51a41fcfaba0ae8ab8c411d187710c53d5a2cc3c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 17 Apr 2015 19:52:48 +0300 Subject: [PATCH 239/290] Fixes Travis errors --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f674040..2eb3d94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,5 @@ before_script: script: - ctest -M Experimental -T Start -T Build -T Test -T Submit - -after_script: - - make check --keep-going # If ctest built the project unsuccessfully, `make check` will report errors to Travis + - make all --keep-going && make check # If ctest built the project unsuccessfully, `make all && make check` will report errors to Travis From 144a10540583db352155c387cc101607bc18c44f Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Fri, 17 Apr 2015 23:33:44 +0600 Subject: [PATCH 240/290] Removes redundant checks from MethodCompiler::shouldProtectProducer() et al. --- src/MethodCompiler.cpp | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index d724894..46b9c89 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -140,7 +140,7 @@ Value* MethodCompiler::protectProducerNode(TJITContext& jit, st::ControlNode* no class Detector { public: - Detector(st::PathVerifier& verifier) : m_verifier(verifier), m_detected(false) {} + Detector() : m_detected(false) {} bool isDetected() const { return m_detected; } @@ -151,26 +151,6 @@ class Detector { m_detected = true; return st::GraphWalker::vrStopWalk; - - /* / Walking through the nodes seeking for consumer - // If it is found then candidate node may affect - // the consumer. Procucer should be protected. - m_verifier.run(candidate); - - if (m_verifier.isVerified()) { - // We found a node that may cause GC and it is really - // on the path between procducer and one of the consumers. - // We've just proved that the value should be protected. - - m_detected = true; - return st::GraphWalker::vrStopWalk; - - } else { - // Node may cause GC but control flow will never reach any of tracked consumers. - // This means that we may safely ignore this node and all it's subpaths. - - return st::GraphWalker::vrSkipPath; - } */ } } else if (node->getNodeType() == st::ControlNode::ntPhi) { // Phi node may not cause gc, protects it's value separately @@ -185,7 +165,6 @@ class Detector { } private: -// st::PathVerifier& m_verifier; bool m_detected; }; @@ -282,10 +261,7 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) // a complex case that affects several domains and probably phi nodes. // We need to perform a generic lookup walking through the whole graph. - st::PathVerifier verifier(consumers); - verifier.addStopNode(producer); - - Detector detector(verifier); + Detector detector; Walker walker(detector); walker.addStopNode(producer); @@ -308,16 +284,6 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) // outs() << "Producer " << producer->getIndex() << " is safe and do not need a protection (2)\n"; return false; - - // Changing direction to forward and performing last check starting from procucer. - // We need to reset the stop list because now we'll use differennt edges. -// walker.resetStopNodes(); -// -// // When performing a forward run we need to end at consumers -// walker.addStopNodes(consumers); -// walker.run(producer, st::GraphWalker::wdForward); - -// return detector.isDetected(); } void MethodCompiler::writePreamble(TJITContext& jit, bool isBlock) From 553c2701e01dfbf20c1d3b6c9a28261ce47ca255 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 18 Apr 2015 19:00:09 +0600 Subject: [PATCH 241/290] Provides Jit>>once: for one-time JIT execution of a block Issue: #74 --- image/imageSource.st | 7 ++- include/jit.h | 9 +++- src/JITRuntime.cpp | 25 ++++++--- src/MethodCompiler.cpp | 115 ++++++++++++++++++++++++----------------- src/vm.cpp | 5 ++ 5 files changed, 106 insertions(+), 55 deletions(-) diff --git a/image/imageSource.st b/image/imageSource.st index b2bd749..d5e93ad 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -1551,7 +1551,12 @@ sendMessage: aSelector withArguments: anArguments METHOD MetaJit do: aBlock - self sendMessage: #value withArguments: (Array with: aBlock). + ^self sendMessage: #value withArguments: (Array with: aBlock). +! + +METHOD MetaJit +once: aBlock + <247 aBlock> ! METHOD MetaJit diff --git a/include/jit.h b/include/jit.h index 68641c8..d22df5e 100644 --- a/include/jit.h +++ b/include/jit.h @@ -296,7 +296,10 @@ class MethodCompiler { void doPushTemporary(TJITContext& jit); void doPushLiteral(TJITContext& jit); void doPushConstant(TJITContext& jit); + void doPushBlock(TJITContext& jit); + llvm::Function* compileBlock(TJITContext& jit, const std::string& blockFunctionName, st::ParsedBlock* parsedBlock); + void doAssignTemporary(TJITContext& jit); void doAssignInstance(TJITContext& jit); void doMarkArguments(TJITContext& jit); @@ -340,6 +343,8 @@ class MethodCompiler { llvm::Value** contextHolder = 0 ); + llvm::Function* compileBlock(TBlock* block); + // TStackObject is a pair of entities allocated on a thread stack space // objectSlot is a container for actual object's data // objectHolder is a pointer to the objectSlot which is registered in GC roots @@ -407,7 +412,6 @@ class JITRuntime { TObject* sendMessage(TContext* callingContext, TSymbol* message, TObjectArray* arguments, TClass* receiverClass, uint32_t callSiteIndex = 0); TBlock* createBlock(TContext* callingContext, uint8_t argLocation, uint16_t bytePointer); - TObject* invokeBlock(TBlock* block, TContext* callingContext); friend TObject* newOrdinaryObject(TClass* klass, uint32_t slotSize); friend TByteObject* newBinaryObject(TClass* klass, uint32_t dataSize); @@ -507,6 +511,9 @@ class JITRuntime { void cleanupDirectHolders(llvm::IRBuilder<>& builder, TDirectBlock& directBlock); bool detectLiteralReceiver(llvm::Value* messageArguments); public: + + TObject* invokeBlock(TBlock* block, TContext* callingContext, bool once = false); + void patchHotMethods(); void printMethod(TMethod* method) { std::string functionName = method->klass->name->toString() + ">>" + method->name->toString(); diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index c5affcf..a2bf851 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -286,27 +286,31 @@ void JITRuntime::optimizeFunction(Function* function, bool runModulePass) m_modulePassManager->run(*m_JITModule); } -TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext) +TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext, bool once) { // Guessing the block function name const uint16_t blockOffset = block->blockBytePointer; - TBlockFunction compiledBlockFunction = lookupBlockFunctionInCache(block->method, blockOffset); + TBlockFunction compiledBlockFunction = once ? 0 : lookupBlockFunctionInCache(block->method, blockOffset); + Function* blockFunction = 0; if (! compiledBlockFunction) { std::ostringstream ss; ss << block->method->klass->name->toString() << ">>" << block->method->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); - Function* blockFunction = m_JITModule->getFunction(blockFunctionName); + blockFunction = m_JITModule->getFunction(blockFunctionName); if (!blockFunction) { // Block functions are created when wrapping method gets compiled. // If function was not found then the whole method needs compilation. // Compiling function and storing it to the table for further use - Function* methodFunction = m_methodCompiler->compileMethod(block->method); - blockFunction = m_JITModule->getFunction(blockFunctionName); - if (!methodFunction || !blockFunction) { + +// Function* methodFunction = m_methodCompiler->compileMethod(block->method); +// blockFunction = m_JITModule->getFunction(blockFunctionName); + blockFunction = m_methodCompiler->compileBlock(block); + + if (/*!methodFunction ||*/ !blockFunction) { // Something is really wrong! outs() << "JIT: Fatal error in invokeBlock for " << blockFunctionName << "\n"; std::exit(1); @@ -320,12 +324,19 @@ TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext) } compiledBlockFunction = reinterpret_cast(m_executionEngine->getPointerToFunction(blockFunction)); - updateBlockFunctionCache(block->method, blockOffset, compiledBlockFunction); + + if (!once) + updateBlockFunctionCache(block->method, blockOffset, compiledBlockFunction); } block->previousContext = callingContext->previousContext; TObject* result = compiledBlockFunction(block); + if (once) { + m_executionEngine->freeMachineCodeForFunction(blockFunction); + blockFunction->eraseFromParent(); + } + return result; } diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 46b9c89..7463ce7 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -702,12 +702,9 @@ void MethodCompiler::doPushConstant(TJITContext& jit) void MethodCompiler::doPushBlock(TJITContext& jit) { - st::PushBlockNode* pushBlockNode = jit.currentNode->cast(); - st::ParsedBlock* parsedBlock = pushBlockNode->getParsedBlock(); + st::PushBlockNode* const pushBlockNode = jit.currentNode->cast(); + st::ParsedBlock* const parsedBlock = pushBlockNode->getParsedBlock(); - TJITBlockContext blockContext(this, jit.parsedMethod, parsedBlock); - - // Creating block function named Class>>method@offset const uint16_t blockOffset = parsedBlock->getStartOffset(); std::ostringstream ss; ss << jit.originMethod->klass->name->toString() + ">>" + jit.originMethod->name->toString() << "@" << blockOffset; @@ -720,62 +717,88 @@ void MethodCompiler::doPushBlock(TJITContext& jit) // vis.run(); // } + // If block function is not already created, create it + if (! m_JITModule->getFunction(blockFunctionName)) + compileBlock(jit, blockFunctionName, parsedBlock); + +// outs() << "Done compiling block " << blockFunctionName << "\n"; + + // Create block object and fill it with context information + Value* const args[] = { + jit.getCurrentContext(), // creatingContext + jit.builder->getInt8(jit.currentNode->getInstruction().getArgument()), // arg offset + jit.builder->getInt16(blockOffset) + }; + + Value* blockObject = jit.builder->CreateCall(m_runtimeAPI.createBlock, args); + blockObject = jit.builder->CreateBitCast(blockObject, m_baseTypes.object->getPointerTo()); + blockObject->setName("block."); + + Value* const blockHolder = protectProducerNode(jit, jit.currentNode, blockObject); + setNodeValue(jit, jit.currentNode, blockHolder); +} + +llvm::Function* MethodCompiler::compileBlock(TBlock* block) +{ + TJITContext methodContext(this, block->method); + const uint16_t blockOffset = block->blockBytePointer; + + std::ostringstream ss; + ss << block->method->klass->name->toString() << ">>" << block->method->name->toString() << "@" << blockOffset; + const std::string blockFunctionName = ss.str(); + + st::ParsedBlock* const parsedBlock = methodContext.parsedMethod->getParsedBlockByOffset(blockOffset); + assert(parsedBlock); + + return compileBlock(methodContext, blockFunctionName, parsedBlock); +} + +llvm::Function* MethodCompiler::compileBlock(TJITContext& jit, const std::string& blockFunctionName, st::ParsedBlock* parsedBlock) +{ + const uint16_t blockOffset = parsedBlock->getStartOffset(); + TJITBlockContext blockContext(this, jit.parsedMethod, parsedBlock); + std::vector blockParams; blockParams.push_back(m_baseTypes.block->getPointerTo()); // block object with context information - FunctionType* blockFunctionType = FunctionType::get( + FunctionType* const blockFunctionType = FunctionType::get( m_baseTypes.object->getPointerTo(), // block return value - blockParams, // parameters - false // we're not dealing with vararg + blockParams, // parameters + false // we're not dealing with vararg ); - blockContext.function = m_JITModule->getFunction(blockFunctionName); - if (! blockContext.function) { // Checking if not already created - blockContext.function = cast(m_JITModule->getOrInsertFunction(blockFunctionName, blockFunctionType)); - - blockContext.function->setGC("shadow-stack"); - m_blockFunctions[blockFunctionName] = blockContext.function; - - // Creating the basic block and inserting it into the function - blockContext.preamble = BasicBlock::Create(m_JITModule->getContext(), "blockPreamble", blockContext.function); - blockContext.builder = new IRBuilder<>(blockContext.preamble); - writePreamble(blockContext, /*isBlock*/ true); - scanForBranches(blockContext, blockContext.parsedBlock); + // Creating block function named Class>>method@offset + blockContext.function = cast(m_JITModule->getOrInsertFunction(blockFunctionName, blockFunctionType)); - ss.str(""); - ss << "offset" << blockOffset; - BasicBlock* const blockBody = parsedBlock->getBasicBlockByOffset(blockOffset)->getValue(); // m_targetToBlockMap[blockOffset]; - assert(blockBody); - blockBody->setName(ss.str()); + blockContext.function->setGC("shadow-stack"); + m_blockFunctions[blockFunctionName] = blockContext.function; - blockContext.builder->CreateBr(blockBody); - blockContext.builder->SetInsertPoint(blockBody); + // Creating the basic block and inserting it into the function + blockContext.preamble = BasicBlock::Create(m_JITModule->getContext(), "blockPreamble", blockContext.function); + blockContext.builder = new IRBuilder<>(blockContext.preamble); + writePreamble(blockContext, /*isBlock*/ true); + scanForBranches(blockContext, blockContext.parsedBlock); - writeFunctionBody(blockContext); + std::stringstream ss; + ss.str(""); + ss << "offset" << blockOffset; + BasicBlock* const blockBody = parsedBlock->getBasicBlockByOffset(blockOffset)->getValue(); // m_targetToBlockMap[blockOffset]; + assert(blockBody); + blockBody->setName(ss.str()); -// outs() << *blockContext.function << "\n"; + blockContext.builder->CreateBr(blockBody); + blockContext.builder->SetInsertPoint(blockBody); - // Running optimization passes on a block function - JITRuntime::Instance()->optimizeFunction(blockContext.function, false); + writeFunctionBody(blockContext); -// outs() << *blockContext.function << "\n"; - } + // outs() << *blockContext.function << "\n"; -// outs() << "Done compiling block " << blockFunctionName << "\n"; + // Running optimization passes on a block function + JITRuntime::Instance()->optimizeFunction(blockContext.function, false); - // Create block object and fill it with context information - Value* args[] = { - jit.getCurrentContext(), // creatingContext - jit.builder->getInt8(jit.currentNode->getInstruction().getArgument()), // arg offset - jit.builder->getInt16(blockOffset) - }; - Value* blockObject = jit.builder->CreateCall(m_runtimeAPI.createBlock, args); - blockObject = jit.builder->CreateBitCast(blockObject, m_baseTypes.object->getPointerTo()); - blockObject->setName("block."); + // outs() << *blockContext.function << "\n"; - Value* blockHolder = protectProducerNode(jit, jit.currentNode, blockObject); - setNodeValue(jit, jit.currentNode, blockHolder); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, blockHolder)); + return blockContext.function; } void MethodCompiler::doAssignTemporary(TJITContext& jit) diff --git a/src/vm.cpp b/src/vm.cpp index 6316e8f..1f16203 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -758,6 +758,11 @@ TObject* SmalltalkVM::performPrimitive(uint8_t opcode, hptr& process, return globals.nilObject; } } break; + + case 247: { // Jit once: aBlock + TBlock* const block = ec.stackPop(); + return JITRuntime::Instance()->invokeBlock(block, ec.currentContext, true); + } #endif case 251: { From e9f0057ae2d39aa0418a4c644fc88ec60ff7e40d Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 18 Apr 2015 22:21:58 +0600 Subject: [PATCH 242/290] Surrounds Jit>>once: primitive in try-catch block Issue: #74 --- src/vm.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vm.cpp b/src/vm.cpp index 1f16203..3f02977 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -760,8 +760,17 @@ TObject* SmalltalkVM::performPrimitive(uint8_t opcode, hptr& process, } break; case 247: { // Jit once: aBlock - TBlock* const block = ec.stackPop(); - return JITRuntime::Instance()->invokeBlock(block, ec.currentContext, true); + try { + TBlock* const block = ec.stackPop(); + return JITRuntime::Instance()->invokeBlock(block, ec.currentContext, true); + } catch(TBlockReturn& blockReturn) { + ec.currentContext = blockReturn.targetContext; + return blockReturn.value; + } catch(TContext* errorContext) { + process->context = errorContext; + failed = true; + return globals.nilObject; + } } #endif From 6920d0921aa8586102c88fe678b981c8b39da436 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 18 Apr 2015 22:27:26 +0600 Subject: [PATCH 243/290] Fixes block function cache management in Jit>>once: Issue: #74 --- include/jit.h | 2 +- src/JITRuntime.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/jit.h b/include/jit.h index d22df5e..54f5341 100644 --- a/include/jit.h +++ b/include/jit.h @@ -437,7 +437,6 @@ class JITRuntime { static const unsigned int LOOKUP_CACHE_SIZE = 512; TFunctionCacheEntry m_functionLookupCache[LOOKUP_CACHE_SIZE]; TBlockFunctionCacheEntry m_blockFunctionLookupCache[LOOKUP_CACHE_SIZE]; - uint32_t m_cacheHits; uint32_t m_cacheMisses; uint32_t m_blockCacheHits; @@ -451,6 +450,7 @@ class JITRuntime { TBlockFunction lookupBlockFunctionInCache(TMethod* containerMethod, uint32_t blockOffset); void updateFunctionCache(TMethod* method, TMethodFunction function); void updateBlockFunctionCache(TMethod* containerMethod, uint32_t blockOffset, TBlockFunction function); + void flushBlockFunctionCache(); void initializePassManager(); diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index a2bf851..6398cf1 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -206,6 +206,11 @@ JITRuntime::~JITRuntime() { delete m_methodCompiler; } +void JITRuntime::flushBlockFunctionCache() +{ + std::memset(&m_blockFunctionLookupCache, 0, sizeof(m_blockFunctionLookupCache)); +} + TBlock* JITRuntime::createBlock(TContext* callingContext, uint8_t argLocation, uint16_t bytePointer) { hptr previousContext = m_softVM->newPointer(callingContext); @@ -324,9 +329,7 @@ TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext, bool o } compiledBlockFunction = reinterpret_cast(m_executionEngine->getPointerToFunction(blockFunction)); - - if (!once) - updateBlockFunctionCache(block->method, blockOffset, compiledBlockFunction); + updateBlockFunctionCache(block->method, blockOffset, compiledBlockFunction); } block->previousContext = callingContext->previousContext; @@ -335,6 +338,7 @@ TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext, bool o if (once) { m_executionEngine->freeMachineCodeForFunction(blockFunction); blockFunction->eraseFromParent(); + flushBlockFunctionCache(); } return result; From 7e15506c20007260f76eee0ef2c9e6274d9e14a0 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 18 Apr 2015 22:43:47 +0600 Subject: [PATCH 244/290] Adds jit shell Issue: #74 --- image/imageSource.st | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/image/imageSource.st b/image/imageSource.st index d5e93ad..19dfaf0 100644 --- a/image/imageSource.st +++ b/image/imageSource.st @@ -1559,6 +1559,15 @@ once: aBlock <247 aBlock> ! +METHOD MetaJit +shell | command | + 'You have entered the JIT shell. Use Ctrl+D to exit.' printNl. + + [ command <- String readline: '+>'. command notNil ] + whileTrue: [ command isEmpty + ifFalse: [ ('Jit once: [' + command + ']') doIt printNl ] ] +! + METHOD MetaJit printStatistics <248> From 3bdb8141f1a6d3f291b44b6151089c5c2bfe34d9 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 18 Apr 2015 22:48:11 +0600 Subject: [PATCH 245/290] Reverts stack overflow handler to simplified loop version Issue: #74 --- src/vm.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/vm.cpp b/src/vm.cpp index 3f02977..05760ef 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -132,25 +132,25 @@ template<> hptr SmalltalkVM::newObject(std::size_t /*dataSize*/, void SmalltalkVM::TVMExecutionContext::stackPush(TObject* object) { - assert(object != 0); + assert(object); + //NOTE: boundary check // The Timothy A. Budd's version of compiler produces // bytecode which can overflow the stack of the context - { - uint32_t stackSize = currentContext->stack->getSize(); - if( stackTop >= stackSize ) { - //resize the current stack - hptr newStack = m_vm->newObject(stackSize*2); - std::copy(newStack->getFields(), newStack->getFields()+stackSize, currentContext->stack->getFields()); -// for(uint32_t i = 0; i < stackSize; i++) { -// TObject* value = currentContext->stack->getField(i); -// newStack->putField(i, value); -// } - currentContext->stack = newStack; - std::cerr << currentContext->method->name->toString() << "!"; - //std::cerr << std::endl << "VM: Stack overflow in '" << currentContext->method->name->toString() << "'" << std::endl; - } + + TObjectArray& oldStack = *currentContext->stack; + const uint32_t stackSize = oldStack.getSize(); + + if (stackTop >= stackSize) { + hptr newStack = m_vm->newObject(stackSize + 7); + + for (uint32_t i = 0; i < stackSize; i++) + newStack[i] = oldStack[i]; + + currentContext->stack = newStack; + std::cerr << currentContext->method->name->toString() << "!"; } + currentContext->stack->putField(stackTop++, object); } From eb76c280bc36f0e5f8eccb4a3cc31ed4c9e8f1e8 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 12 May 2015 04:23:12 +0300 Subject: [PATCH 246/290] Adds contrainer-baised builds to cache deb packages --- .travis.yml | 11 ++++++++--- .travis/linux/install-dependencies.sh | 12 ------------ 2 files changed, 8 insertions(+), 15 deletions(-) delete mode 100755 .travis/linux/install-dependencies.sh diff --git a/.travis.yml b/.travis.yml index 2eb3d94..ddbd3d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,16 @@ compiler: os: - linux +sudo: false cache: apt: true +addons: + apt: + packages: + - g++-multilib + - linux-libc-dev:i386 + - libreadline-dev:i386 + - llvm-3.3-dev:i386 notifications: email: false @@ -30,9 +38,6 @@ env: - USE_LLVM=On BUILD_TYPE=Release -before_install: - - ./.travis/${TRAVIS_OS_NAME}/install-dependencies.sh - before_script: - mkdir -p build - cd build diff --git a/.travis/linux/install-dependencies.sh b/.travis/linux/install-dependencies.sh deleted file mode 100755 index 06d200d..0000000 --- a/.travis/linux/install-dependencies.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -sudo apt-get update -qq - -alias apt_install='sudo apt-get -qy --no-install-suggests --no-install-recommends install' - -apt_install "g++-multilib" gcc-multilib linux-libc-dev:i386 # 32bit compilers -apt_install libreadline-dev:i386 - -if [ "$USE_LLVM" = "On" ]; then - apt_install llvm-3.3:i386 llvm-3.3-dev:i386 libllvm3.3:i386 llvm-3.3-runtime:i386 -fi From 679df3c227828648ee1f9cd09fefab930724f22c Mon Sep 17 00:00:00 2001 From: vlastachu Date: Mon, 10 Mar 2014 22:00:23 +0400 Subject: [PATCH 247/290] Adds log for baker memory manager Adds timer class and some fixes in write log functions. Not tested yet. Issue: #47 Conflicts: src/BakerMemoryManager.cpp Adds some Windows specific code Adds timer template header. Not complete. Issue: #47 Completes changes on windows. Issue: #47 Conflicts: include/timer.h src/Timer.cpp Completes changes on linux. Integrates new timer to Baker GC. Changes for testing different gc. Conflicts: src/main.cpp Modified Timer class; Fix magick numbers Add include guard; fix whitespaces Add corretness check for mm_type Avoid C-casts Fix reorder warning Adds constructor TMemoryManagerInfo Fix TMemoryManagerInfo constructor Fix spaces in Timer Fix Timer::now() Fix TMemoryManagerEvent constructor Remove extra newlines Adds constructor for TMemoryManagerHeapEvent remove integral metrics replace IMemoryManager statistical members to m_memoryInfo clean main.cpp add GCLogger fix args constructor Refactor GC classes: logger now have default implementation without any logging and custom loggers now sets from setLogger method. Fix Timer - remove explicit inline; Move GCLogger to IMemoryManager; Replace fstream with ofsteam; Replace bytes to kilobytes cast to GCLogger class Replace define with enum. Remove unusual spaces and newlines. --- CMakeLists.txt | 2 + include/Timer.h | 118 ++++++++++++++++++++++++++++++ include/args.h | 3 +- include/memory.h | 96 ++++++++++++++++++++++-- src/BakerMemoryManager.cpp | 40 +++++----- src/GCLogger.cpp | 39 ++++++++++ src/GenerationalMemoryManager.cpp | 4 +- src/NonCollectMemoryManager.cpp | 28 ++++++- src/Timer.cpp | 70 ++++++++++++++++++ src/args.cpp | 5 ++ src/main.cpp | 27 +++++-- 11 files changed, 391 insertions(+), 41 deletions(-) create mode 100644 include/Timer.h create mode 100644 src/GCLogger.cpp create mode 100644 src/Timer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a089148..af24368 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,8 @@ add_library(standart_set src/ControlGraph.cpp src/ControlGraphVisualizer.cpp + src/Timer.cpp + src/GCLogger.cpp ) if (USE_LLVM) diff --git a/include/Timer.h b/include/Timer.h new file mode 100644 index 0000000..f1131b6 --- /dev/null +++ b/include/Timer.h @@ -0,0 +1,118 @@ + +#ifndef LLST_TIMER_H_INCLUDED +#define LLST_TIMER_H_INCLUDED + +#include +#include +#include +using std::stringstream; +#include +#include + +#if defined(unix) || defined(__unix__) || defined(__unix) + #include + typedef timeval systemTimeValue; +#else + typedef clock_t systemTimeValue; +#endif + + + +//analogue of c++11 ratio from chrono.h +template +struct TRatio +{ + static const int num = NUM; + static const int den = DEN; +}; + +typedef TRatio<86400,1> TDay; +typedef TRatio<3600,1> THour; +typedef TRatio<60,1> TMin; +typedef TRatio<1,1> TSec; +typedef TRatio<1,1000> TMillisec; +typedef TRatio<1,1000000> TMicrosec; +typedef TRatio<1,1000000000> TNanosec; + + +//type used to show string representation of ratio +enum SuffixMode {SNONE, SSHORT, SFULL}; + +//analogue of c++11 duration +template +class TDuration { +private: + double value; +public: + //argument: duration in target ratio value + TDuration(double duration){ + value = duration; + } + + TDuration() {value = 0;} + + bool isEmpty() { return value == 0;} + + template TDuration convertTo(){ + return TDuration(value * (RATIO::num * RATIO2::den) / (double)(RATIO::den * RATIO2::num)); + } + int toInt(){ + return floor(value); + } + double toDouble(){ + return value; + } + std::string toString(SuffixMode sMode = SNONE, int symbolsAfterPoint = 0, + const char* pointSymbol = ".", const char* spaceSymbol = " "){ + stringstream ss; + ss << floor(value); + if(symbolsAfterPoint) + ss << pointSymbol << std::setfill('0') << std::setw(symbolsAfterPoint) + << floor((value - floor(value)) * pow(10.0, symbolsAfterPoint)); + if(sMode != SNONE) + ss << spaceSymbol << getSuffix(sMode); + return ss.str(); + } + std::string getSuffix(SuffixMode sMode); + + std::ostream& operator<<(std::ostream& os){ + os << toString(); + return os; + } + + bool operator< (const TDuration& rhs){ + return value < rhs.value; + } + + TDuration operator+(const TDuration& rhs){ + return TDuration(value + rhs.value); + } + + TDuration operator-(const TDuration& rhs){ + return TDuration(value - rhs.value); + } + + bool operator> (const TDuration& rhs){return !(operator< (rhs));} + + friend class Timer; +}; + + +class Timer { +private: + systemTimeValue timeCreate; + double getDiffSec(); +public: + //timer which count time from specified unix-time. + Timer(time_t time); + //default constructor is implicit Timer::now() + Timer(){ this->start();} + static Timer now() {Timer t; return t;} + void start(); + template + TDuration get(){ + return TDuration(getDiffSec()).convertTo(); + } +}; + +#endif diff --git a/include/args.h b/include/args.h index b44a6d1..ce8b4a1 100644 --- a/include/args.h +++ b/include/args.h @@ -43,10 +43,11 @@ struct args std::size_t heapSize; std::size_t maxHeapSize; std::string imagePath; + std::string memoryManagerType; int showHelp; int showVersion; args() : - heapSize(0), maxHeapSize(0), showHelp(false), showVersion(false) + heapSize(0), maxHeapSize(0), memoryManagerType(), showHelp(false), showVersion(false) { } void parse(int argc, char **argv); diff --git a/include/memory.h b/include/memory.h index 3c80acc..41b505f 100644 --- a/include/memory.h +++ b/include/memory.h @@ -37,10 +37,80 @@ #include #include +#include #include #include #include #include +#include "Timer.h" + + + +struct TMemoryManagerHeapEvent { + const std::string eventName; + TDuration timeDiff; + uint32_t usedHeapSizeBeforeCollect; + uint32_t usedHeapSizeAfterCollect; + uint32_t totalHeapSize; + TMemoryManagerHeapEvent() : eventName(), timeDiff(), + usedHeapSizeBeforeCollect(0), usedHeapSizeAfterCollect(0), totalHeapSize(0) {} + TMemoryManagerHeapEvent(std::string name) : eventName(name), timeDiff(), + usedHeapSizeBeforeCollect(0), usedHeapSizeAfterCollect(0), totalHeapSize(0) {} +}; + +struct TMemoryManagerHeapInfo { + uint32_t usedHeapSizeBeforeCollect; + uint32_t usedHeapSizeAfterCollect; + uint32_t totalHeapSize; + std::list heapEvents; + TMemoryManagerHeapInfo() : + usedHeapSizeBeforeCollect(0), + usedHeapSizeAfterCollect(0), + totalHeapSize(0), + heapEvents() + {} + bool empty() const { + return usedHeapSizeBeforeCollect == 0 && + usedHeapSizeAfterCollect == 0 && + totalHeapSize == 0 && + heapEvents.empty(); + } +}; + +//represent three kinds of events in garbage collection log: +//just event, event which takes some time, event which interacting with a heap +struct TMemoryManagerEvent { + const std::string eventName; + TDuration begin; //time spent from program start to event begin + TDuration timeDiff; //maybe null + TMemoryManagerHeapInfo heapInfo; //maybe empty + TMemoryManagerEvent(const std::string name): eventName(name), begin(), timeDiff(), heapInfo() {} +}; + +class IGCLogger { +public: + virtual void writeLogLine(TMemoryManagerEvent event) = 0; + virtual ~IGCLogger() {}; +}; + +class GCLogger : public IGCLogger { +private: + std::ofstream m_logFile; +public: + GCLogger(const char* fileName); + virtual ~GCLogger(); + virtual void writeLogLine(TMemoryManagerEvent event); +}; + +class EmptyGCLogger : public IGCLogger +{ +public: + EmptyGCLogger(){} + virtual void writeLogLine(TMemoryManagerEvent){} + virtual ~EmptyGCLogger() {} +}; + + // Memory manager statics is held @@ -53,6 +123,11 @@ struct TMemoryManagerInfo { uint32_t leftToRightCollections; uint32_t rightToLeftCollections; uint64_t rightCollectionDelay; + Timer timer; + std::list events; + TMemoryManagerInfo():collectionsCount(0), allocationsCount(0), totalCollectionDelay(0), + leftToRightCollections(0), rightToLeftCollections(0), rightCollectionDelay(0), + timer(), events(){} }; struct object_ptr { @@ -69,7 +144,14 @@ struct object_ptr { // Custom implementations such as BakerMemoryManager // implement this interface. class IMemoryManager { +protected: + std::tr1::shared_ptr m_gcLogger; + IMemoryManager(): m_gcLogger(new EmptyGCLogger()){} public: + virtual void setLogger(std::tr1::shared_ptr logger){ + m_gcLogger = logger; + } + virtual bool initializeHeap(std::size_t heapSize, std::size_t maxSize = 0) = 0; virtual bool initializeStaticHeap(std::size_t staticHeapSize) = 0; @@ -194,10 +276,7 @@ template <> class hptr : public hptr_base { class BakerMemoryManager : public IMemoryManager { protected: - uint32_t m_collectionsCount; - uint32_t m_allocationsCount; - uint64_t m_totalCollectionDelay; - + TMemoryManagerInfo m_memoryInfo; std::size_t m_heapSize; std::size_t m_maxHeapSize; @@ -214,6 +293,7 @@ class BakerMemoryManager : public IMemoryManager uint8_t* m_staticHeapBase; uint8_t* m_staticHeapPointer; + struct TRootPointers { uint32_t size; uint32_t top; @@ -272,7 +352,7 @@ class BakerMemoryManager : public IMemoryManager // Returns amount of allocations that were done after last GC // May be used as a flag that GC had just took place - virtual uint32_t allocsBeyondCollection() { return m_allocationsCount; } + virtual uint32_t allocsBeyondCollection() { return m_memoryInfo.allocationsCount; } virtual TMemoryManagerInfo getStat(); }; @@ -309,6 +389,9 @@ class GenerationalMemoryManager : public BakerMemoryManager class NonCollectMemoryManager : public IMemoryManager { protected: + TMemoryManagerInfo m_memoryInfo; + std::auto_ptr m_gcLogger; + size_t m_heapSize; uint8_t* m_heapBase; uint8_t* m_heapPointer; @@ -339,7 +422,7 @@ class NonCollectMemoryManager : public IMemoryManager virtual void releaseExternalHeapPointer(object_ptr& /*pointer*/) {} virtual bool checkRoot(TObject* /*value*/, TObject** /*objectSlot*/) { return false; } virtual uint32_t allocsBeyondCollection() { return 0; } - virtual TMemoryManagerInfo getStat() { return TMemoryManagerInfo(); } + virtual TMemoryManagerInfo getStat(); }; class LLVMMemoryManager : public BakerMemoryManager { @@ -440,3 +523,4 @@ class Image::ImageWriter }; #endif + diff --git a/src/BakerMemoryManager.cpp b/src/BakerMemoryManager.cpp index c2f4739..fad1c4a 100644 --- a/src/BakerMemoryManager.cpp +++ b/src/BakerMemoryManager.cpp @@ -46,14 +46,11 @@ bool is_aligned_properly(uint32_t x) { } BakerMemoryManager::BakerMemoryManager() : - m_collectionsCount(0), m_allocationsCount(0), m_totalCollectionDelay(0), - m_heapSize(0), m_maxHeapSize(0), m_heapOne(0), m_heapTwo(0), + m_memoryInfo(), m_heapSize(0), m_maxHeapSize(0), m_heapOne(0), m_heapTwo(0), m_activeHeapOne(true), m_inactiveHeapBase(0), m_inactiveHeapPointer(0), m_activeHeapBase(0), m_activeHeapPointer(0), m_staticHeapSize(0), m_staticHeapBase(0), m_staticHeapPointer(0), m_externalPointersHead(0) -{ - // Nothing to be done here -} +{} BakerMemoryManager::~BakerMemoryManager() { @@ -198,7 +195,7 @@ void* BakerMemoryManager::allocate(std::size_t requestedSize, bool* gcOccured /* assert( is_aligned_properly(result) ); if (gcOccured && !*gcOccured) - m_allocationsCount++; + m_memoryInfo.allocationsCount++; return result; } @@ -379,10 +376,15 @@ BakerMemoryManager::TMovableObject* BakerMemoryManager::moveObject(TMovableObjec } } + void BakerMemoryManager::collectGarbage() { - m_collectionsCount++; - + //get statistic before collect + m_memoryInfo.collectionsCount++; + TMemoryManagerEvent event("GC"); + event.begin = m_memoryInfo.timer.get(); + event.heapInfo.usedHeapSizeBeforeCollect = (m_heapSize/2 - (m_activeHeapPointer - m_activeHeapBase)); + event.heapInfo.totalHeapSize = m_heapSize; // First of all swapping the spaces if (m_activeHeapOne) { @@ -402,21 +404,18 @@ void BakerMemoryManager::collectGarbage() // objects down the hierarchy to find active objects. // Then moving them to the new active heap. - // Storing timestamp on start - timeval tv1; - gettimeofday(&tv1, NULL); - // Moving the live objects in the new heap moveObjects(); - // Storing timestamp of the end - timeval tv2; - gettimeofday(&tv2, NULL); std::memset(m_inactiveHeapBase, 0, m_heapSize / 2); // Calculating total microseconds spent in the garbage collection procedure - m_totalCollectionDelay += (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec); + event.heapInfo.usedHeapSizeAfterCollect = (m_heapSize/2 - (m_activeHeapPointer - m_activeHeapBase)); + event.timeDiff = m_memoryInfo.timer.get() - event.begin; + m_memoryInfo.totalCollectionDelay += event.timeDiff.convertTo().toInt(); + m_memoryInfo.events.push_front(event); + m_gcLogger->writeLogLine(event); } void BakerMemoryManager::moveObjects() @@ -529,11 +528,6 @@ void BakerMemoryManager::releaseExternalHeapPointer(object_ptr& pointer) { TMemoryManagerInfo BakerMemoryManager::getStat() { - TMemoryManagerInfo info; - std::memset(&info, 0, sizeof(info)); - - info.allocationsCount = m_allocationsCount; - info.collectionsCount = m_collectionsCount; - info.totalCollectionDelay = m_totalCollectionDelay; - return info; + return m_memoryInfo; } + diff --git a/src/GCLogger.cpp b/src/GCLogger.cpp new file mode 100644 index 0000000..e80b9ca --- /dev/null +++ b/src/GCLogger.cpp @@ -0,0 +1,39 @@ +#include "memory.h" + + +GCLogger::GCLogger(const char* fileName): + m_logFile(fileName, std::fstream::out) +{} + +GCLogger::~GCLogger(){ + m_logFile.flush(); +} + +enum MeasuringConstants { bytes_in_kb = 1024 }; + + +void GCLogger::writeLogLine(TMemoryManagerEvent event){ + m_logFile << event.begin.toString(SNONE, 3) + << ": [" << event.eventName << " "; + if(!event.heapInfo.empty()){ + TMemoryManagerHeapInfo eh = event.heapInfo; + m_logFile << eh.usedHeapSizeBeforeCollect / bytes_in_kb << "K->" + << eh.usedHeapSizeAfterCollect / bytes_in_kb << "K(" + << eh.totalHeapSize / bytes_in_kb << "K)"; + for(std::list::iterator i = eh.heapEvents.begin(); i != eh.heapEvents.end(); i++){ + m_logFile << "[" << i->eventName << ": " + << i->usedHeapSizeBeforeCollect / bytes_in_kb << "K->" + << i->usedHeapSizeAfterCollect / bytes_in_kb << "K(" + << i->totalHeapSize / bytes_in_kb << "K)"; + if(!i->timeDiff.isEmpty()) + m_logFile << ", " << i->timeDiff.toString(SSHORT, 6); + m_logFile << "] "; + } + } + if(!event.timeDiff.isEmpty()) + m_logFile << ", " << event.timeDiff.toString(SSHORT, 6); + //gc-viewer see error when no delay or delay is 0.0 + else + m_logFile << ", 0.000001 secs"; + m_logFile << "]\n"; +} diff --git a/src/GenerationalMemoryManager.cpp b/src/GenerationalMemoryManager.cpp index 6141c86..630c2f8 100644 --- a/src/GenerationalMemoryManager.cpp +++ b/src/GenerationalMemoryManager.cpp @@ -126,9 +126,9 @@ void GenerationalMemoryManager::collectGarbage() gettimeofday(&tv2, NULL); // Calculating total microseconds spent in the garbage collection procedure - m_totalCollectionDelay += (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec); + m_memoryInfo.totalCollectionDelay += (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec); - m_collectionsCount++; + m_memoryInfo.collectionsCount++; } void GenerationalMemoryManager::collectLeftToRight(bool fullCollect /*= false*/) diff --git a/src/NonCollectMemoryManager.cpp b/src/NonCollectMemoryManager.cpp index 9910999..74f30fd 100644 --- a/src/NonCollectMemoryManager.cpp +++ b/src/NonCollectMemoryManager.cpp @@ -41,12 +41,13 @@ #include #include #include +#include + NonCollectMemoryManager::NonCollectMemoryManager() : - m_heapSize(0), m_heapBase(0), m_heapPointer(0), + m_memoryInfo(), m_heapSize(0), m_heapBase(0), m_heapPointer(0), m_staticHeapSize(0), m_staticHeapBase(0), m_staticHeapPointer(0) -{ -} +{} NonCollectMemoryManager::~NonCollectMemoryManager() { @@ -55,6 +56,7 @@ NonCollectMemoryManager::~NonCollectMemoryManager() free( m_usedHeaps[i] ); } + bool NonCollectMemoryManager::initializeStaticHeap(size_t staticHeapSize) { staticHeapSize = correctPadding(staticHeapSize); @@ -92,6 +94,10 @@ bool NonCollectMemoryManager::initializeHeap(size_t heapSize, size_t /*maxSize*/ void NonCollectMemoryManager::growHeap() { + TMemoryManagerEvent event("GC"); + event.heapInfo.usedHeapSizeBeforeCollect = m_usedHeaps.size()*m_heapSize; + m_memoryInfo.collectionsCount++; + event.begin = m_memoryInfo.timer.get(); uint8_t* heap = static_cast( std::malloc(m_heapSize) ); if (!heap) { std::printf("MM: Cannot allocate %zu bytes\n", m_heapSize); @@ -104,6 +110,14 @@ void NonCollectMemoryManager::growHeap() m_heapPointer = heap + m_heapSize; m_usedHeaps.push_back(heap); + + //there is no collecting: after = before + event.heapInfo.usedHeapSizeAfterCollect = event.heapInfo.usedHeapSizeBeforeCollect; + event.heapInfo.totalHeapSize = m_usedHeaps.size()*m_heapSize; + event.timeDiff = m_memoryInfo.timer.get() - event.begin; + m_memoryInfo.totalCollectionDelay += event.timeDiff.convertTo().toInt(); + m_memoryInfo.events.push_front(event); + m_gcLogger->writeLogLine(event); } void* NonCollectMemoryManager::allocate(size_t requestedSize, bool* gcOccured /*= 0*/ ) @@ -119,6 +133,8 @@ void* NonCollectMemoryManager::allocate(size_t requestedSize, bool* gcOccured /* } m_heapPointer -= requestedSize; + + m_memoryInfo.allocationsCount++; return m_heapPointer; } @@ -138,3 +154,9 @@ bool NonCollectMemoryManager::isInStaticHeap(void* location) { return (location >= m_staticHeapPointer) && (location < m_staticHeapBase + m_staticHeapSize); } + +TMemoryManagerInfo NonCollectMemoryManager::getStat() { + return m_memoryInfo; + +} + diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..20e60e9 --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,70 @@ + +#include "Timer.h" +#include +#include +using std::stringstream; + + +#if defined(unix) || defined(__unix__) || defined(__unix) +Timer::Timer(time_t _time){ + time_t current; + time(¤t); + time_t diff = current - _time; + timeval cur_tv; + gettimeofday(&cur_tv, 0); + timeval result = {cur_tv.tv_sec - diff, 0}; + timeCreate = result; +} + +void Timer::start(){ + gettimeofday(&timeCreate, 0); +} + +double Timer::getDiffSec(){ + timeval current; + gettimeofday(¤t, 0); + double diff = current.tv_sec + current.tv_usec/static_cast(TMicrosec::den) + - (timeCreate.tv_sec + timeCreate.tv_usec/static_cast(TMicrosec::den)); + return diff; +} +#else +Timer::Timer(time_t _time){ + time_t current; + time(¤t); + clock_t diff = (current - _time)*CLOCKS_PER_SEC; + timeCreate = clock() - diff; +} + + +void Timer::start(){ + timeCreate = clock(); +} + +double Timer::getDiffSec(){ + return static_cast((clock() - timeCreate))/CLOCKS_PER_SEC; +} +#endif + +template <> std::string TDuration::getSuffix(SuffixMode sMode){ + return sMode == SFULL ? "days" : sMode == SSHORT ? "days" : ""; } + +template <> std::string TDuration::getSuffix(SuffixMode sMode){ + return sMode == SFULL ? "hours" : sMode == SSHORT ? "hours" : ""; } + +template <> std::string TDuration::getSuffix(SuffixMode sMode){ + return sMode == SFULL ? "minutes" : sMode == SSHORT ? "mins" : ""; } + +template <> std::string TDuration::getSuffix(SuffixMode sMode){ + return sMode == SFULL ? "seconds" : sMode == SSHORT ? "secs" : ""; } + +template <> std::string TDuration::getSuffix(SuffixMode sMode){ + return sMode == SFULL ? "milliseconds" : sMode == SSHORT ? "msecs" : ""; } + +template <> std::string TDuration::getSuffix(SuffixMode sMode){ + return sMode == SFULL ? "microseconds" : sMode == SSHORT ? "mcsecs" : ""; } + +template <> std::string TDuration::getSuffix(SuffixMode sMode){ + return sMode == SFULL ? "nanoseconds" : sMode == SSHORT ? "usecs" : ""; } + + + diff --git a/src/args.cpp b/src/args.cpp index d6d5dbb..f9a4f95 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -46,6 +46,7 @@ void args::parse(int argc, char **argv) image = 'i', heap_max = 'H', heap = 'h', + mm_type = 'm', getopt_set_arg = 0, getopt_err = '?', @@ -57,6 +58,7 @@ void args::parse(int argc, char **argv) {"heap_max", required_argument, 0, heap_max}, {"heap", required_argument, 0, heap}, {"image", required_argument, 0, image}, + {"mm_type", required_argument, 0, mm_type}, {"help", no_argument, 0, help}, {"version", no_argument, 0, version}, {0, 0, 0, 0} @@ -77,6 +79,9 @@ void args::parse(int argc, char **argv) case image: { imagePath = optarg; } break; + case mm_type: { + memoryManagerType = optarg; + } break; case heap: { bool good_number = std::istringstream( optarg ) >> heapSize; if (!good_number) diff --git a/src/main.cpp b/src/main.cpp index 91e07c9..a7b4b12 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -79,13 +80,27 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } -#if defined(LLVM) - std::auto_ptr memoryManager(new LLVMMemoryManager()); -#else - std::auto_ptr memoryManager(new BakerMemoryManager()); -#endif + IMemoryManager* mm; + if(llstArgs.memoryManagerType == "nc") { + mm = new NonCollectMemoryManager(); + } + else if(llstArgs.memoryManagerType == "" || llstArgs.memoryManagerType == "copy") { + #if defined(LLVM) + mm = new LLVMMemoryManager(); + #else + mm = new BakerMemoryManager(); + #endif + } + else{ + std::cout << "error: wrong option --mm_type=" << llstArgs.memoryManagerType << ";\n" + << "defined options for memory manager type:\n" + << "\"copy\" (default) - copying garbage collector;\n" + << "\"nc\" - non-collecting memory manager.\n"; + return EXIT_FAILURE; + } + std::auto_ptr memoryManager(mm); memoryManager->initializeHeap(llstArgs.heapSize, llstArgs.maxHeapSize); - + memoryManager->setLogger(std::tr1::shared_ptr(new GCLogger("gc.log"))); std::auto_ptr smalltalkImage(new Image(memoryManager.get())); smalltalkImage->loadImage(llstArgs.imagePath); From db679fdb5ea57089709fa48ea2708b06c13b831c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 30 May 2015 18:35:19 +0300 Subject: [PATCH 248/290] Builds project for Coveralls, improves cmake --- .travis.yml | 12 ++++++++++-- CMakeLists.txt | 2 ++ cmake/variables.cmake | 2 ++ tests/CMakeLists.txt | 16 ++++++++-------- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index ddbd3d0..62e82d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,8 @@ os: sudo: false cache: - apt: true + - apt + - pip addons: apt: packages: @@ -37,6 +38,11 @@ env: BUILD_TYPE=Debug - USE_LLVM=On BUILD_TYPE=Release + - USE_LLVM=Off + BUILD_TYPE=Coverage + +install: + - pip install --user cpp-coveralls before_script: - mkdir -p build @@ -45,5 +51,7 @@ before_script: script: - ctest -M Experimental -T Start -T Build -T Test -T Submit - - make all --keep-going && make check # If ctest built the project unsuccessfully, `make all && make check` will report errors to Travis + - cmake --build . -- all --keep-going && cmake --build . --target check # If ctest built the project unsuccessfully, `make all && make check` will report errors to Travis +after_success: + - if [ ${CXX} == g++ ] && [ ${BUILD_TYPE} == Coverage ]; then ~/.local/bin/coveralls --repo_token "INxVunXhVXbQWjVbwoIisKeXSJqCRGnI2" --exclude build/tests/gtest -E ".*CMake.*CompilerId.c" --gcov-options "\-lp" -r ../; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index af24368..d29ef55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,8 @@ option(BUILD_TESTS "Should we build tests?" OFF) if (BUILD_TESTS) enable_testing() configure_file( "${CMAKE_SOURCE_DIR}/cmake/CTestCustom.cmake.in" "${CMAKE_BINARY_DIR}/CTestCustom.cmake" @ONLY) + set (CTEST_SUBMIT_RETRY_DELAY 0 CACHE STRING "How long to wait between timed-out CTest submissions.") + set (CTEST_SUBMIT_RETRY_COUNT 0 CACHE STRING "How many times to retry timed-out CTest submissions.") include(CTest) add_subdirectory(tests) endif() diff --git a/cmake/variables.cmake b/cmake/variables.cmake index e12831c..26e21e7 100644 --- a/cmake/variables.cmake +++ b/cmake/variables.cmake @@ -5,6 +5,7 @@ set (CMAKE_C_FLAGS_DEBUG_INIT "-ggdb3 -O0") set (CMAKE_C_FLAGS_MINSIZEREL_INIT "-Os") set (CMAKE_C_FLAGS_RELEASE_INIT "-O3 -DNDEBUG") set (CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "-O2 -g") +set (CMAKE_C_FLAGS_COVERAGE "--coverage -fkeep-inline-functions") # C++ compiler. set (CMAKE_CXX_FLAGS_INIT "-Wall -Wextra -fexceptions -frtti") @@ -12,6 +13,7 @@ set (CMAKE_CXX_FLAGS_DEBUG_INIT "-ggdb3 -O0") set (CMAKE_CXX_FLAGS_MINSIZEREL_INIT "-Os") set (CMAKE_CXX_FLAGS_RELEASE_INIT "-O3 -DNDEBUG") set (CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-O2 -g") +set (CMAKE_CXX_FLAGS_COVERAGE "--coverage -fkeep-inline-functions") # Flags used by the linker. set (CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT "-s") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index abf47db..cea7c78 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,7 +4,7 @@ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) macro(cxx_test pretty_name bin_name sources libs) add_executable(${bin_name} ${sources}) - target_link_libraries(${bin_name} ${libs} ${GTEST_BOTH_LIBRARIES}) + target_link_libraries(${bin_name} supc++ -pthread ${libs} ${GTEST_BOTH_LIBRARIES} ${READLINE_LIBS_TO_LINK}) set_target_properties(${bin_name} PROPERTIES COMPILE_DEFINITIONS TESTS_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\") add_test(${pretty_name} ${bin_name}) add_dependencies(check ${bin_name}) @@ -12,10 +12,10 @@ endmacro() include_directories(${CMAKE_SOURCE_DIR}/include ${GTEST_INCLUDE_DIR}) -cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;-pthread") -cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;-pthread") -cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;-pthread") -cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;-pthread") -cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;-pthread") -# TODO cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi;-pthread") -cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set;-pthread") +cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi") +cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi") +cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi") +cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi") +cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi") +# TODO cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi") +cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set") From f164150ab12e8a24809da53320b58664834e0623 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 30 May 2015 20:12:25 +0300 Subject: [PATCH 249/290] Adds coveralls and travis badges --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d5ff2d5..084f020 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![Build Status](https://travis-ci.org/0x7CFE/llst.svg?branch=develop)](https://travis-ci.org/0x7CFE/llst) +[![Coverage Status](https://coveralls.io/repos/0x7CFE/llst/badge.svg?branch=develop)](https://coveralls.io/r/0x7CFE/llst?branch=develop) + Overview ================= LLST stands for LLVM Smalltalk or Low Level Smalltalk (which intends it's use in embedded environments). From c8778d9efaffd346e927e5368d0a45227523753f Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 30 Jun 2015 15:19:44 +0300 Subject: [PATCH 250/290] Fixes dead code in ABAB test --- tests/abab_problem.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/abab_problem.cpp b/tests/abab_problem.cpp index 791330a..78ed50c 100644 --- a/tests/abab_problem.cpp +++ b/tests/abab_problem.cpp @@ -28,6 +28,7 @@ INSTANTIATE_TEST_CASE_P(_, P_DecodeBytecode, void checkSendBinaryArg(st::InstructionNode* inst, int idx) { + ASSERT_TRUE(idx == 0 || idx == 1); st::ControlNode* arg = inst->getArgument(idx); { SCOPED_TRACE("Each argument of sendBinary is a phi node"); @@ -49,8 +50,6 @@ void checkSendBinaryArg(st::InstructionNode* inst, int idx) case 1: { phisToCheck.push_back(phiArg); } break; - default: - FAIL() << "idx should be 0 or 1"; } ASSERT_GT(phisToCheck.size(), 0u); @@ -58,12 +57,13 @@ void checkSendBinaryArg(st::InstructionNode* inst, int idx) for(std::size_t phiIdx = 0; phiIdx < phisToCheck.size(); phiIdx++) { SCOPED_TRACE("Each edge of arg-phi is a PushConstant"); st::PhiNode* phi = phisToCheck[phiIdx]; - for(st::TNodeSet::iterator i = phi->getInEdges().begin(); i != phi->getInEdges().end(); i++) { - st::ControlNode* edge = *i; - ASSERT_EQ( st::ControlNode::ntInstruction, edge->getNodeType() ); - if (st::InstructionNode* edgeInst = edge->cast()) { - ASSERT_EQ( opcode::pushConstant, edgeInst->getInstruction().getOpcode() ); - } + st::PhiNode::TIncomingList incomingList = phi->getIncomingList(); + for(size_t i = 0; i < incomingList.size(); i++) { + st::PhiNode::TIncoming incoming = incomingList[i]; + ASSERT_EQ( st::ControlNode::ntInstruction, incoming.node->getNodeType() ); + st::InstructionNode* inst = incoming.node->cast(); + ASSERT_FALSE(inst == NULL); + ASSERT_EQ( opcode::pushConstant, inst->getInstruction().getOpcode() ); } } } From 3251e6f0da75d1978ae255adb03c6a7d79591292 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 1 Jul 2015 12:28:03 +0300 Subject: [PATCH 251/290] Removes assert(false) --- include/analysis.h | 8 +++--- src/ControlGraph.cpp | 4 +-- src/MethodCompiler.cpp | 26 +++++++++---------- src/ParsedBytecode.cpp | 48 +++++++++++++++-------------------- src/TSmalltalkInstruction.cpp | 14 ++++------ 5 files changed, 44 insertions(+), 56 deletions(-) diff --git a/include/analysis.h b/include/analysis.h index afaa1d3..9d3f436 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -341,6 +341,11 @@ class ControlGraph { nodes_iterator nodes_end() { return m_nodes.end(); } ControlNode* newNode(ControlNode::TNodeType type) { + assert(type == ControlNode::ntInstruction + || type == ControlNode::ntPhi + || type == ControlNode::ntTau + ); + ControlNode* node = 0; switch (type) { @@ -355,9 +360,6 @@ class ControlGraph { case ControlNode::ntTau: node = new TauNode(m_lastNodeIndex); break; - - default: - assert(false); } m_lastNodeIndex++; diff --git a/src/ControlGraph.cpp b/src/ControlGraph.cpp index 2e97a94..60c8ee6 100644 --- a/src/ControlGraph.cpp +++ b/src/ControlGraph.cpp @@ -514,12 +514,12 @@ class GraphOptimizer : public PlainNodeVisitor { // Removing nodes that were optimized out TNodeList::iterator iNode = m_nodesToRemove.begin(); for (; iNode != m_nodesToRemove.end(); ++iNode) { + assert((*iNode)->getNodeType() == ControlNode::ntInstruction + || (*iNode)->getNodeType() == ControlNode::ntPhi); if (InstructionNode* const instruction = (*iNode)->cast()) removeInstruction(instruction); else if (PhiNode* const phi = (*iNode)->cast()) removePhi(phi); - else - assert(false); } } diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 7463ce7..4291ec0 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -145,6 +145,10 @@ class Detector { bool isDetected() const { return m_detected; } st::GraphWalker::TVisitResult checkNode(st::ControlNode* node) { + assert(node->getNodeType() == st::ControlNode::ntInstruction + || node->getNodeType() == st::ControlNode::ntPhi + ); + if (st::InstructionNode* const candidate = node->cast()) { if (candidate->getInstruction().mayCauseGC()) { outs() << "Detector noticed node " << candidate->getIndex() << " that may cause GC\n"; @@ -157,10 +161,7 @@ class Detector { // and do not have outgoing edges that we may traverse. return st::GraphWalker::vrSkipPath; - } else { - assert(false); } - return st::GraphWalker::vrKeepWalking; } @@ -238,17 +239,14 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) return false; } - if (st::InstructionNode* const candidate = node->cast()) { - if (candidate->getInstruction().mayCauseGC()) { -// outs() << "Producer " << producer->getIndex() -// << " should be protected because node " -// << candidate->getIndex() << " may cause GC\n"; - - return true; - } - } else { - // There should be instruction nodes only - assert(false); + assert(node->getNodeType() == st::ControlNode::ntInstruction); + st::InstructionNode* const candidate = node->cast(); + if (candidate->getInstruction().mayCauseGC()) { +// outs() << "Producer " << producer->getIndex() +// << " should be protected because node " +// << candidate->getIndex() << " may cause GC\n"; + + return true; } assert(node->getOutEdges().size() == 1); diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index d29e98d..3e1a146 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -148,13 +148,11 @@ void ParsedBytecode::parse(uint16_t startOffset, uint16_t stopOffset) { // Final check for the last instruction of the method if (decoder.getBytePointer() >= stopPointer && instruction.isBranch()) { const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(instruction.getExtra()); - if (iTargetBlock != m_offsetToBasicBlock.end()) { - iTargetBlock->second->getReferers().insert(currentBasicBlock); + assert(iTargetBlock != m_offsetToBasicBlock.end()); - if (traces_enabled) - std::printf("%.4u : block reference %p (%u) ->? %p (%u)\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); - } else - assert(false); + iTargetBlock->second->getReferers().insert(currentBasicBlock); + if (traces_enabled) + std::printf("%.4u : block reference %p (%u) ->? %p (%u)\n", currentBytePointer, currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); } } @@ -212,16 +210,14 @@ void ParsedBytecode::eraseBasicBlock(ParsedBytecode::iterator& iBlock) void ParsedBytecode::eraseReferer(uint16_t targetOffset, BasicBlock* referer) { const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(targetOffset); - if (iTargetBlock != m_offsetToBasicBlock.end()) { - BasicBlock* const target = iTargetBlock->second; + assert(iTargetBlock != m_offsetToBasicBlock.end()); - if (traces_enabled) - std::printf("erasing reference %p (%u) -> %p (%u)\n", referer, referer->getOffset(), target, target->getOffset()); + BasicBlock* const target = iTargetBlock->second; - target->getReferers().erase(referer); - } else { - assert(false); - } + if (traces_enabled) + std::printf("erasing reference %p (%u) -> %p (%u)\n", referer, referer->getOffset(), target, target->getOffset()); + + target->getReferers().erase(referer); } uint16_t ParsedBytecode::getNextBlockOffset(BasicBlock* currentBlock, uint16_t stopOffset) { @@ -258,14 +254,12 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* if (terminator.getArgument() == special::branch) { // Unconditional branch case const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); - if (iTargetBlock != m_offsetToBasicBlock.end()) { - if (traces_enabled) - std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + assert(iTargetBlock != m_offsetToBasicBlock.end()); - iTargetBlock->second->getReferers().insert(currentBasicBlock); - } else { - assert(false); - } + if (traces_enabled) + std::printf("%.4u : block reference %p (%u) -> %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + + iTargetBlock->second->getReferers().insert(currentBasicBlock); } else { // Previous block referred some other block instead. // Terminator is one of conditional branch instructions. @@ -281,14 +275,12 @@ void ParsedBytecode::updateReferences(BasicBlock* currentBasicBlock, BasicBlock* // Case when branch condition is met const TOffsetToBasicBlockMap::iterator iTargetBlock = m_offsetToBasicBlock.find(terminator.getExtra()); - if (iTargetBlock != m_offsetToBasicBlock.end()) { - if (traces_enabled) - std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + assert(iTargetBlock != m_offsetToBasicBlock.end()); - iTargetBlock->second->getReferers().insert(currentBasicBlock); - } else { - assert(false); - } + if (traces_enabled) + std::printf("%.4u : block reference %p (%u) ->T %p (%u)\n", decoder.getBytePointer(), currentBasicBlock, currentBasicBlock->getOffset(), iTargetBlock->second, iTargetBlock->first); + + iTargetBlock->second->getReferers().insert(currentBasicBlock); } } } else { diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 1b0c905..ca949f6 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -37,6 +37,7 @@ bool st::TSmalltalkInstruction::isBranch() const } bool st::TSmalltalkInstruction::isValueProvider() const { + assert(m_opcode != opcode::extended); switch (m_opcode) { case opcode::pushInstance: case opcode::pushArgument: @@ -71,11 +72,9 @@ bool st::TSmalltalkInstruction::isValueProvider() const { return false; } - case opcode::extended: - assert(false); + default: + return false; } - - return false; } bool st::TSmalltalkInstruction::isTrivial() const { @@ -131,6 +130,7 @@ bool st::TSmalltalkInstruction::mayCauseGC() const { } bool st::TSmalltalkInstruction::isValueConsumer() const { + assert(m_opcode != opcode::extended); switch (m_opcode) { case opcode::pushInstance: case opcode::pushArgument: @@ -162,13 +162,9 @@ bool st::TSmalltalkInstruction::isValueConsumer() const { // TODO User defined primitives return true; - case opcode::extended: default: - assert(false); + return false; } - - assert(false); - return false; } std::string st::TSmalltalkInstruction::toString() const From bc470c231468c641469cb3d4b307ff0fbf785a56 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 1 Jul 2015 13:48:08 +0300 Subject: [PATCH 252/290] Adds more tests on TSmalltalkInstruction --- src/TSmalltalkInstruction.cpp | 39 +++-------------- tests/tsmalltalk_instruction.cpp | 73 ++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index ca949f6..92dc653 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -52,24 +52,11 @@ bool st::TSmalltalkInstruction::isValueProvider() const { case opcode::doPrimitive: return true; - case opcode::assignTemporary: - case opcode::assignInstance: - return false; - case opcode::doSpecial: switch (m_argument) { case special::duplicate: case special::sendToSuper: return true; - - case special::selfReturn: - case special::stackReturn: - case special::blockReturn: - case special::popTop: - case special::branch: - case special::branchIfTrue: - case special::branchIfFalse: - return false; } default: @@ -107,23 +94,17 @@ bool st::TSmalltalkInstruction::mayCauseGC() const { return false; switch (m_opcode) { - case opcode::assignTemporary: - case opcode::assignInstance: - return false; - case opcode::pushBlock: // case opcode::sendUnary: case opcode::sendBinary: case opcode::sendMessage: + case opcode::doPrimitive: return true; case opcode::doSpecial: // The only special that may cause GC return m_argument == special::sendToSuper; - case opcode::doPrimitive: - return true; - default: return false; } @@ -132,14 +113,6 @@ bool st::TSmalltalkInstruction::mayCauseGC() const { bool st::TSmalltalkInstruction::isValueConsumer() const { assert(m_opcode != opcode::extended); switch (m_opcode) { - case opcode::pushInstance: - case opcode::pushArgument: - case opcode::pushTemporary: - case opcode::pushLiteral: - case opcode::pushConstant: - case opcode::pushBlock: - return false; - case opcode::assignTemporary: case opcode::assignInstance: case opcode::sendUnary: @@ -148,6 +121,11 @@ bool st::TSmalltalkInstruction::isValueConsumer() const { case opcode::markArguments: return true; + case opcode::doPrimitive: + // All system primitives consume a value + // TODO User defined primitives + return true; + case opcode::doSpecial: switch (m_argument) { case special::branch: @@ -157,11 +135,6 @@ bool st::TSmalltalkInstruction::isValueConsumer() const { return true; } - case opcode::doPrimitive: - // All system primitives consume a value - // TODO User defined primitives - return true; - default: return false; } diff --git a/tests/tsmalltalk_instruction.cpp b/tests/tsmalltalk_instruction.cpp index ae210df..035d9f4 100644 --- a/tests/tsmalltalk_instruction.cpp +++ b/tests/tsmalltalk_instruction.cpp @@ -90,6 +90,79 @@ TEST(TSmalltalkInstruction, isValueConsumer) EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).isValueConsumer()); EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::selfReturn).isValueConsumer()); } + { + SCOPED_TRACE("stack writers are not consumers"); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushInstance).isValueConsumer()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushArgument).isValueConsumer()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushTemporary).isValueConsumer()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushLiteral).isValueConsumer()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushBlock).isValueConsumer()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushConstant).isValueConsumer()); + } +} + +TEST(TSmalltalkInstruction, isTrivial) +{ + { + SCOPED_TRACE("trivial"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushInstance).isTrivial()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushArgument).isTrivial()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushTemporary).isTrivial()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushLiteral).isTrivial()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushConstant).isTrivial()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::duplicate).isTrivial()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::markArguments).isTrivial()); + } + { + SCOPED_TRACE("the other are not trivial"); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushBlock).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branch).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::popTop).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::stackReturn).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::selfReturn).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::sendToSuper).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::sendMessage).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::sendUnary).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::sendBinary).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignTemporary).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignInstance).isTrivial()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doPrimitive).isTrivial()); + } +} + +TEST(TSmalltalkInstruction, mayCauseGC) +{ + { + SCOPED_TRACE("may cause gc"); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::pushBlock).mayCauseGC()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendMessage).mayCauseGC()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::sendBinary).mayCauseGC()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doPrimitive).mayCauseGC()); + EXPECT_TRUE(st::TSmalltalkInstruction(opcode::doSpecial, special::sendToSuper).mayCauseGC()); + } + { + SCOPED_TRACE("never cause gc"); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushInstance).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushArgument).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushTemporary).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushLiteral).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::pushConstant).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::branch).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::popTop).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::stackReturn).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::selfReturn).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::doSpecial, special::duplicate).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::sendUnary).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignTemporary).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::assignInstance).mayCauseGC()); + EXPECT_FALSE(st::TSmalltalkInstruction(opcode::markArguments).mayCauseGC()); + } } TEST(TSmalltalkInstruction, serializeIsInverseToCtor) From 0726fdfb10a07c413d1513cff1b54bf18aec4049 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 1 Jul 2015 13:56:09 +0300 Subject: [PATCH 253/290] Fixes bug in TSmalltalkInstruction::mayCauseGC --- src/TSmalltalkInstruction.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/TSmalltalkInstruction.cpp b/src/TSmalltalkInstruction.cpp index 92dc653..e2db87f 100644 --- a/src/TSmalltalkInstruction.cpp +++ b/src/TSmalltalkInstruction.cpp @@ -90,9 +90,6 @@ bool st::TSmalltalkInstruction::mayCauseGC() const { // NOTE We expect that markArguments is encoded // directly, so no heap allocation occur - if (isTrivial() || isTerminator()) - return false; - switch (m_opcode) { case opcode::pushBlock: // case opcode::sendUnary: From 9cf967242a145f523666f2c5f14b26e00a1f0a54 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 1 Jul 2015 14:08:12 +0300 Subject: [PATCH 254/290] Adds test on TSmalltalkInstruction::toString --- tests/tsmalltalk_instruction.cpp | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/tsmalltalk_instruction.cpp b/tests/tsmalltalk_instruction.cpp index 035d9f4..e405652 100644 --- a/tests/tsmalltalk_instruction.cpp +++ b/tests/tsmalltalk_instruction.cpp @@ -165,6 +165,42 @@ TEST(TSmalltalkInstruction, mayCauseGC) } } +TEST(TSmalltalkInstruction, toString) +{ + { + SCOPED_TRACE("nonexistent instructions"); + EXPECT_ANY_THROW(st::TSmalltalkInstruction(42).toString()); + EXPECT_ANY_THROW(st::TSmalltalkInstruction(opcode::doSpecial, 42).toString()); + EXPECT_ANY_THROW(st::TSmalltalkInstruction(opcode::sendUnary, 42).toString()); + EXPECT_ANY_THROW(st::TSmalltalkInstruction(opcode::sendBinary, 42).toString()); + EXPECT_ANY_THROW(st::TSmalltalkInstruction(opcode::pushConstant, 42).toString()); + } + { + SCOPED_TRACE("existent instructions"); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::pushBlock).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::pushInstance).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::pushArgument).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::pushTemporary).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::pushLiteral).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::pushConstant).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::sendMessage).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::sendBinary).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::sendUnary).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doPrimitive).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::assignTemporary).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::assignInstance).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::markArguments).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfTrue).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::branchIfFalse).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::branch).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::popTop).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::stackReturn).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::blockReturn).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::selfReturn).toString()); + EXPECT_NO_THROW(st::TSmalltalkInstruction(opcode::doSpecial, special::duplicate).toString()); + } +} + TEST(TSmalltalkInstruction, serializeIsInverseToCtor) { using namespace opcode; From 7d6e614b38f3f06c6f23ff2649f3fbac0ca0d687 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 1 Jul 2015 23:56:12 +0300 Subject: [PATCH 255/290] Improve code coverage for tests/helpers/ControlGraph.h --- tests/helpers/ControlGraph.h | 46 ++++++++++-------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index 0a4e699..ee9825d 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -13,8 +13,7 @@ class H_LastInstIsTerminator: public st::BasicBlockVisitor H_LastInstIsTerminator(st::ParsedBytecode* parsedBytecode) : st::BasicBlockVisitor(parsedBytecode) {} virtual bool visitBlock(st::BasicBlock& BB) { std::size_t bbSize = BB.size(); - if (bbSize == 0) - return true; + EXPECT_NE(0u, bbSize); st::TSmalltalkInstruction terminator(0); bool hasTerminator = BB.getTerminator(terminator); @@ -34,18 +33,13 @@ class H_LastInstIsTerminator: public st::BasicBlockVisitor } { SCOPED_TRACE("There must be no terminators but the last one"); - uint32_t terminatorsCount = 0; st::BasicBlock::iterator iInstruction = BB.begin(); st::BasicBlock::iterator iEnd = BB.end()-1; // except the last inst while (iInstruction != iEnd) { - bool isTerminator = (*iInstruction).isTerminator(); - if (isTerminator) - terminatorsCount++; - EXPECT_FALSE(isTerminator); + EXPECT_FALSE((*iInstruction).isTerminator()); ++iInstruction; } - EXPECT_EQ(0u, terminatorsCount); } return true; @@ -60,9 +54,8 @@ class H_DomainHasTerminator: public st::DomainVisitor st::InstructionNode* terminator = domain.getTerminator(); { SCOPED_TRACE("Each domain must have a terminator"); - if (!terminator) - EXPECT_TRUE( terminator != NULL ); - else + EXPECT_TRUE( terminator != NULL ); + if (terminator) EXPECT_TRUE( terminator->getInstruction().isTerminator() ); } return true; @@ -76,9 +69,7 @@ class H_AreBBsLinked: public st::NodeVisitor virtual bool visitDomain(st::ControlDomain& domain) { st::BasicBlock* currentBB = domain.getBasicBlock(); if (currentBB->getOffset() != 0) { - EXPECT_GT(currentBB->getReferers().size(), 0u) - << "All BB but the 1st must have referrers. " - << "BB offset: " << currentBB->getOffset(); + EXPECT_GT(currentBB->getReferers().size(), 0u) << "All BB but the 1st must have referrers. BB offset: " << currentBB->getOffset(); } st::ControlDomain::iterator iNode = domain.begin(); const st::ControlDomain::iterator iEnd = domain.end(); @@ -133,8 +124,7 @@ class H_AreBBsLinked: public st::NodeVisitor st::ControlDomain* targetDomain = target->getDomain(); EXPECT_EQ(target, targetDomain->getEntryPoint()); uint16_t targetOffset = targetDomain->getBasicBlock()->getOffset(); - EXPECT_EQ(targetOffset, branch.getExtra()) - << "Unconditional branch must point exactly to its the only one out edge"; + EXPECT_EQ(targetOffset, branch.getExtra()) << "Unconditional branch must point exactly to its the only one out edge"; st::BasicBlock::TBasicBlockSet& referrers = target->getDomain()->getBasicBlock()->getReferers(); EXPECT_NE(referrers.end(), referrers.find(currentBB)); } @@ -204,9 +194,7 @@ class H_CorrectNumOfEdges: public st::PlainNodeVisitor EXPECT_GT(incomingList.size(), 0u) << "The phi must have at least 1 incoming edge"; EXPECT_GE(outEdges.size(), 1u) << "There must be a node using the given phi"; } - if (st::TauNode* tau = node.cast()) { - EXPECT_TRUE(tau == NULL /* always fails */); // TODO - } + EXPECT_NE(st::ControlNode::ntTau, node.getNodeType()); // TODO: Tau logic is not done yet return true; } }; @@ -225,11 +213,7 @@ class H_NoOrphans { allNodes.begin(), allNodes.end(), std::back_inserter(orphans) ); - for (TOrphans::const_iterator it = orphans.begin(); it != orphans.end(); ++it) { - st::ControlNode* node = *it; - FAIL() << "Orphan node index: " << node->getIndex() - << " from BB offset: " << node->getDomain()->getBasicBlock()->getOffset(); - } + EXPECT_EQ(0u, orphans.size()); } private: st::TNodeSet getLinkedNodes() const { @@ -283,8 +267,9 @@ class H_ConsumeProvider: public st::PlainNodeVisitor for(std::size_t i = 0; i < argSize; ++i) { st::ControlNode* argNode = instNode->getArgument(i); if (st::InstructionNode* arg = argNode->cast()) { - EXPECT_TRUE(arg->getInstruction().isValueProvider()) - << "'" << arg->getInstruction().toString() << "' should provide value for '" << inst.toString() << "'"; + std::string provider = arg->getInstruction().toString(); + std::string consumer = inst.toString(); + EXPECT_TRUE(arg->getInstruction().isValueProvider()) << "'" << provider << "' should provide value for '" << consumer << "'"; } } } @@ -292,11 +277,7 @@ class H_ConsumeProvider: public st::PlainNodeVisitor if (st::PhiNode* phiNode = node.cast()) { const st::TNodeSet& inEdges = phiNode->getInEdges(); for(st::TNodeSet::const_iterator it = inEdges.begin(); it != inEdges.end(); ++it) { - st::ControlNode* edge = *it; - if (st::InstructionNode* inst = edge->cast()) { - EXPECT_TRUE(inst->getInstruction().isValueProvider()) - << "'" << inst->getInstruction().toString() << "' should provide value for 'Phi index: " << phiNode->getPhiIndex() << "'"; - } + EXPECT_NE(st::ControlNode::ntInstruction, (*it)->getNodeType()); } } return true; @@ -349,8 +330,7 @@ class H_NonUniqueIncomingsOfPhi: public st::PlainNodeVisitor st::PhiNode::TIncomingList incomingList = phi->getIncomingList(); st::PhiNode::TIncomingList::iterator uniqueEnd = std::unique(incomingList.begin(), incomingList.end(), CompareIncoming::cmp); std::size_t uniqueSize = std::distance(incomingList.begin(), uniqueEnd); - EXPECT_EQ(uniqueSize, phi->getIncomingList().size()) - << "The incomings of phi must differ between each other"; + EXPECT_EQ(uniqueSize, phi->getIncomingList().size()) << "The incomings of phi must differ between each other"; } return true; } From ee251c758280fbb7ab7f901d4df91e9fcc61c864 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 2 Jul 2015 00:33:28 +0300 Subject: [PATCH 256/290] Updates radix_tree, add tests for radix_tree --- include/radix_tree/radix_tree.hpp | 696 ++++++++---------- include/radix_tree/radix_tree_it.hpp | 121 ++- include/radix_tree/radix_tree_node.hpp | 57 +- tests/CMakeLists.txt | 2 + tests/radix_tree/CMakeLists.txt | 9 + tests/radix_tree/common.hpp | 43 ++ tests/radix_tree/test_radix_tree_erase.cpp | 130 ++++ tests/radix_tree/test_radix_tree_find.cpp | 49 ++ .../test_radix_tree_greedy_match.cpp | 80 ++ tests/radix_tree/test_radix_tree_insert.cpp | 120 +++ tests/radix_tree/test_radix_tree_iterator.cpp | 110 +++ .../test_radix_tree_longest_match.cpp | 71 ++ .../test_radix_tree_prefix_match.cpp | 96 +++ 13 files changed, 1113 insertions(+), 471 deletions(-) create mode 100644 tests/radix_tree/CMakeLists.txt create mode 100644 tests/radix_tree/common.hpp create mode 100644 tests/radix_tree/test_radix_tree_erase.cpp create mode 100644 tests/radix_tree/test_radix_tree_find.cpp create mode 100644 tests/radix_tree/test_radix_tree_greedy_match.cpp create mode 100644 tests/radix_tree/test_radix_tree_insert.cpp create mode 100644 tests/radix_tree/test_radix_tree_iterator.cpp create mode 100644 tests/radix_tree/test_radix_tree_longest_match.cpp create mode 100644 tests/radix_tree/test_radix_tree_prefix_match.cpp diff --git a/include/radix_tree/radix_tree.hpp b/include/radix_tree/radix_tree.hpp index 0ec1acb..8ec1128 100644 --- a/include/radix_tree/radix_tree.hpp +++ b/include/radix_tree/radix_tree.hpp @@ -6,545 +6,495 @@ #include #include -#ifdef DEBUG -#include -#endif - #include "radix_tree_it.hpp" #include "radix_tree_node.hpp" -static -std::string -radix_substr(const std::string &str, int begin, int num) -{ - return str.substr(begin, num); -} +template +K radix_substr(const K &key, int begin, int num); -static -int -radix_length(const std::string &str) { - return str.size(); +template<> +inline std::string radix_substr(const std::string &key, int begin, int num) +{ + return key.substr(begin, num); } -template -class radix_tree { -public: - typedef K key_type; - typedef T mapped_type; - typedef std::pair value_type; - typedef radix_tree_it iterator; - typedef std::size_t size_type; - - - radix_tree() : m_size(0), m_root(NULL) { } - ~radix_tree() { if (m_root != NULL) delete m_root; } - - size_type size() const { return m_size; } - bool empty() const { return m_size == 0; } - void clear() { delete m_root; m_root = NULL; m_size = 0; } +template +K radix_join(const K &key1, const K &key2); - iterator find(const K &key); - iterator begin(); - iterator end(); - - std::pair insert(const value_type &val); - size_type erase(const K &key); - void erase(iterator it); - void prefix_match(const K &key, - std::vector &vec); - void greedy_match(const K &key, - std::vector &vec); - iterator longest_match(const K &key); - - - T& operator[] (const K &lhs); - -private: - size_type m_size; - radix_tree_node* m_root; - - radix_tree_node* begin(radix_tree_node *node); - radix_tree_node* find_node(const K &key, - radix_tree_node *node, - int depth); - radix_tree_node* append(radix_tree_node *parent, - const value_type &val); - radix_tree_node* prepend(radix_tree_node *node, - const value_type &val); - void greedy_match(radix_tree_node *node, - std::vector &vec); - - -#ifdef DEBUG -public: - void print_nodes(); -private: - void print_nodes(radix_tree_node *node, std::string space); -#endif -}; +template<> +inline std::string radix_join(const std::string &key1, const std::string &key2) +{ + return key1 + key2; +} +template +int radix_length(const K &key); -#ifdef DEBUG -template -void -radix_tree::print_nodes() +template<> +inline int radix_length(const std::string &key) { - if (m_root == NULL) - return; - - std::cout << "$root" << std::endl; - print_nodes(m_root, " "); + return key.size(); } template -void -radix_tree::print_nodes(radix_tree_node *node, std::string space) -{ - typename std::map*>::iterator it; - - for (it = node->m_children.begin(); it != node->m_children.end(); - ++it) { - if (it->first.size() > 0) { - std::cout << space << it->first << std::endl; - } else { - std::cout << space << "$end, " - << it->second->m_value->second << std::endl; - } +class radix_tree { +public: + typedef K key_type; + typedef T mapped_type; + typedef std::pair value_type; + typedef radix_tree_it iterator; + typedef std::size_t size_type; + + radix_tree() : m_size(0), m_root(NULL) { } + ~radix_tree() { + if (m_root != NULL) delete m_root; + } + + size_type size() const { + return m_size; + } + bool empty() const { + return m_size == 0; + } + void clear() { + delete m_root; + m_root = NULL; + m_size = 0; + } + + iterator find(const K &key); + iterator begin(); + iterator end(); + + std::pair insert(const value_type &val); + bool erase(const K &key); + void erase(iterator it); + void prefix_match(const K &key, std::vector &vec); + void greedy_match(const K &key, std::vector &vec); + iterator longest_match(const K &key); + + T& operator[] (const K &lhs); - print_nodes(it->second, space + " "); - } -} -#endif +private: + size_type m_size; + radix_tree_node* m_root; + + radix_tree_node* begin(radix_tree_node *node); + radix_tree_node* find_node(const K &key, radix_tree_node *node, int depth); + radix_tree_node* append(radix_tree_node *parent, const value_type &val); + radix_tree_node* prepend(radix_tree_node *node, const value_type &val); + void greedy_match(radix_tree_node *node, std::vector &vec); +}; template -void -radix_tree::prefix_match(const K &key, std::vector &vec) +void radix_tree::prefix_match(const K &key, std::vector &vec) { - vec.clear(); + vec.clear(); - if (m_root == NULL) - return; + if (m_root == NULL) + return; - radix_tree_node *node; - K key_sub1, key_sub2; + radix_tree_node *node; + K key_sub1, key_sub2; - node = find_node(key, m_root, 0); + node = find_node(key, m_root, 0); - if (node->m_is_leaf) - node = node->m_parent; + if (node->m_is_leaf) + node = node->m_parent; - int len = radix_length(key) - node->m_depth; - key_sub1 = radix_substr(key, node->m_depth, len); - key_sub2 = radix_substr(node->m_key, 0, len); + int len = radix_length(key) - node->m_depth; + key_sub1 = radix_substr(key, node->m_depth, len); + key_sub2 = radix_substr(node->m_key, 0, len); - if (key_sub1 != key_sub2) - return; + if (key_sub1 != key_sub2) + return; - greedy_match(node, vec); + greedy_match(node, vec); } template -typename radix_tree::iterator -radix_tree::longest_match(const K &key) +typename radix_tree::iterator radix_tree::longest_match(const K &key) { - if (m_root == NULL) - return iterator(NULL); + if (m_root == NULL) + return iterator(NULL); - radix_tree_node *node; - K key_sub; + radix_tree_node *node; + K key_sub; - node = find_node(key, m_root, 0); + node = find_node(key, m_root, 0); - if (node->m_is_leaf) - return iterator(node); + if (node->m_is_leaf) + return iterator(node); - key_sub = radix_substr(key, node->m_depth, radix_length(node->m_key)); + key_sub = radix_substr(key, node->m_depth, radix_length(node->m_key)); - if (! (key_sub == node->m_key)) - node = node->m_parent; + if (! (key_sub == node->m_key)) + node = node->m_parent; - K nul = radix_substr(key, 0, 0); + K nul = radix_substr(key, 0, 0); - while (node != NULL) { - typename radix_tree_node::it_child it; - it = node->m_children.find(nul); - if (it != node->m_children.end() && it->second->m_is_leaf) - return iterator(it->second); + while (node != NULL) { + typename radix_tree_node::it_child it; + it = node->m_children.find(nul); + if (it != node->m_children.end() && it->second->m_is_leaf) + return iterator(it->second); - node = node->m_parent; - } + node = node->m_parent; + } - return iterator(NULL); + return iterator(NULL); } template -typename radix_tree::iterator -radix_tree::end() +typename radix_tree::iterator radix_tree::end() { - return iterator(NULL); + return iterator(NULL); } template -typename radix_tree::iterator -radix_tree::begin() +typename radix_tree::iterator radix_tree::begin() { - radix_tree_node *node; + radix_tree_node *node; - if (m_root == NULL) - node = NULL; - else - node = begin(m_root); + if (m_root == NULL) + node = NULL; + else + node = begin(m_root); - return iterator(node); + return iterator(node); } template -radix_tree_node* -radix_tree::begin(radix_tree_node *node) +radix_tree_node* radix_tree::begin(radix_tree_node *node) { - if (node->m_is_leaf) - return node; + if (node->m_is_leaf) + return node; - assert(node->m_children.size() > 0); + assert(!node->m_children.empty()); - return begin(node->m_children.begin()->second); + return begin(node->m_children.begin()->second); } template -T& -radix_tree::operator[] (const K &lhs) +T& radix_tree::operator[] (const K &lhs) { - iterator it = find(lhs); + iterator it = find(lhs); - if (it == end()) { - std::pair val; - val.first = lhs; + if (it == end()) { + std::pair val; + val.first = lhs; - std::pair ret; - ret = insert(val); + std::pair ret; + ret = insert(val); - assert(ret.second == true); + assert(ret.second == true); - it = ret.first; - } + it = ret.first; + } - return it->second; + return it->second; } template -void -radix_tree::greedy_match(const K &key, std::vector &vec) +void radix_tree::greedy_match(const K &key, std::vector &vec) { - radix_tree_node *node; + radix_tree_node *node; - vec.clear(); + vec.clear(); - if (m_root == NULL) - return; + if (m_root == NULL) + return; - node = find_node(key, m_root, 0); + node = find_node(key, m_root, 0); - if (node->m_is_leaf) - node = node->m_parent; + if (node->m_is_leaf) + node = node->m_parent; - greedy_match(node, vec); + greedy_match(node, vec); } template -void -radix_tree::greedy_match(radix_tree_node *node, - std::vector &vec) +void radix_tree::greedy_match(radix_tree_node *node, std::vector &vec) { - if (node->m_is_leaf) { - vec.push_back(iterator(node)); - return; - } + if (node->m_is_leaf) { + vec.push_back(iterator(node)); + return; + } - typename std::map*>::iterator it; + typename std::map*>::iterator it; - for (it = node->m_children.begin(); it != node->m_children.end(); - ++it) { - greedy_match(it->second, vec); - } + for (it = node->m_children.begin(); it != node->m_children.end(); ++it) { + greedy_match(it->second, vec); + } } template -void -radix_tree::erase(iterator it) +void radix_tree::erase(iterator it) { - erase(it->first); + erase(it->first); } template -typename radix_tree::size_type -radix_tree::erase(const K &key) +bool radix_tree::erase(const K &key) { - if (m_root == NULL) - return 0; + if (m_root == NULL) + return 0; + + radix_tree_node *child; + radix_tree_node *parent; + radix_tree_node *grandparent; + K nul = radix_substr(key, 0, 0); - radix_tree_node *child; - radix_tree_node *parent; - radix_tree_node *grandparent; - K nul = radix_substr(key, 0, 0); + child = find_node(key, m_root, 0); - child = find_node(key, m_root, 0); + if (! child->m_is_leaf) + return 0; - if (! child->m_is_leaf) - return 0; + parent = child->m_parent; + parent->m_children.erase(nul); - parent = child->m_parent; - parent->m_children.erase(nul); + delete child; - delete child; + m_size--; - m_size--; + if (parent == m_root) + return 1; - if (parent == m_root) - return 1; + if (parent->m_children.size() > 1) + return 1; + if (parent->m_children.empty()) { grandparent = parent->m_parent; grandparent->m_children.erase(parent->m_key); - delete parent; + } else { + grandparent = parent; + } - if (grandparent == m_root) { - return 1; - } + if (grandparent == m_root) { + return 1; + } - if (grandparent->m_children.size() == 1) { - // merge grandparent with the uncle - typename std::map*>::iterator it; - it = grandparent->m_children.begin(); + if (grandparent->m_children.size() == 1) { + // merge grandparent with the uncle + typename std::map*>::iterator it; + it = grandparent->m_children.begin(); - radix_tree_node *uncle = it->second; + radix_tree_node *uncle = it->second; - uncle->m_depth = parent->m_depth; - uncle->m_key = radix_join(grandparent->m_key, uncle->m_key); - uncle->m_parent = grandparent->m_parent; + if (uncle->m_is_leaf) + return 1; - grandparent->m_children.erase(it); + uncle->m_depth = grandparent->m_depth; + uncle->m_key = radix_join(grandparent->m_key, uncle->m_key); + uncle->m_parent = grandparent->m_parent; - grandparent->m_parent->m_children.erase(grandparent->m_key); - grandparent->m_parent->m_children[uncle->m_key] = uncle; + grandparent->m_children.erase(it); - delete grandparent; - } + grandparent->m_parent->m_children.erase(grandparent->m_key); + grandparent->m_parent->m_children[uncle->m_key] = uncle; - return 1; + delete grandparent; + } + + return 1; } template -radix_tree_node* -radix_tree::append(radix_tree_node *parent, const value_type &val) +radix_tree_node* radix_tree::append(radix_tree_node *parent, const value_type &val) { - int depth; - int len; - K nul = radix_substr(val.first, 0, 0); - radix_tree_node *node_c, *node_cc; + int depth; + int len; + K nul = radix_substr(val.first, 0, 0); + radix_tree_node *node_c, *node_cc; - depth = parent->m_depth + radix_length(parent->m_key); - len = radix_length(val.first) - depth; + depth = parent->m_depth + radix_length(parent->m_key); + len = radix_length(val.first) - depth; - if (len == 0) { - node_c = new radix_tree_node(val); + if (len == 0) { + node_c = new radix_tree_node(val); - node_c->m_depth = depth; - node_c->m_parent = parent; - node_c->m_key = nul; - node_c->m_is_leaf = true; + node_c->m_depth = depth; + node_c->m_parent = parent; + node_c->m_key = nul; + node_c->m_is_leaf = true; - parent->m_children[nul] = node_c; + parent->m_children[nul] = node_c; - return node_c; - } else { - node_c = new radix_tree_node(val); + return node_c; + } else { + node_c = new radix_tree_node(val); - K key_sub = radix_substr(val.first, depth, len); + K key_sub = radix_substr(val.first, depth, len); - parent->m_children[key_sub] = node_c; + parent->m_children[key_sub] = node_c; - node_c->m_depth = depth; - node_c->m_parent = parent; - node_c->m_key = key_sub; + node_c->m_depth = depth; + node_c->m_parent = parent; + node_c->m_key = key_sub; - node_cc = new radix_tree_node(val); - node_c->m_children[nul] = node_cc; + node_cc = new radix_tree_node(val); + node_c->m_children[nul] = node_cc; - node_cc->m_depth = depth + len; - node_cc->m_parent = node_c; - node_cc->m_key = nul; - node_cc->m_is_leaf = true; + node_cc->m_depth = depth + len; + node_cc->m_parent = node_c; + node_cc->m_key = nul; + node_cc->m_is_leaf = true; - return node_cc; - } + return node_cc; + } } template -radix_tree_node* -radix_tree::prepend(radix_tree_node *node, const value_type &val) +radix_tree_node* radix_tree::prepend(radix_tree_node *node, const value_type &val) { - int count; - int len1, len2; + int count; + int len1, len2; - len1 = radix_length(node->m_key); - len2 = radix_length(val.first) - node->m_depth; + len1 = radix_length(node->m_key); + len2 = radix_length(val.first) - node->m_depth; - for (count = 0; count < len1 && count < len2; count++) { - if (! (node->m_key[count] == - val.first[count + node->m_depth])) - break; - } + for (count = 0; count < len1 && count < len2; count++) { + if (! (node->m_key[count] == val.first[count + node->m_depth]) ) + break; + } - assert(count != 0); + assert(count != 0); + node->m_parent->m_children.erase(node->m_key); - node->m_parent->m_children.erase(node->m_key); + radix_tree_node *node_a = new radix_tree_node; + node_a->m_parent = node->m_parent; + node_a->m_key = radix_substr(node->m_key, 0, count); + node_a->m_depth = node->m_depth; + node_a->m_parent->m_children[node_a->m_key] = node_a; - radix_tree_node *node_a = new radix_tree_node; - node_a->m_parent = node->m_parent; - node_a->m_key = radix_substr(node->m_key, 0, count); - node_a->m_depth = node->m_depth; - node_a->m_parent->m_children[node_a->m_key] = node_a; + node->m_depth += count; + node->m_parent = node_a; + node->m_key = radix_substr(node->m_key, count, len1 - count); + node->m_parent->m_children[node->m_key] = node; + K nul = radix_substr(val.first, 0, 0); + if (count == len2) { + radix_tree_node *node_b; - node->m_depth += count; - node->m_parent = node_a; - node->m_key = radix_substr(node->m_key, count, len1 - count); - node->m_parent->m_children[node->m_key] = node; + node_b = new radix_tree_node(val); + node_b->m_parent = node_a; + node_b->m_key = nul; + node_b->m_depth = node_a->m_depth + count; + node_b->m_is_leaf = true; + node_b->m_parent->m_children[nul] = node_b; - K nul = radix_substr(val.first, 0, 0); - if (count == len2) { - radix_tree_node *node_b; + return node_b; + } else { + radix_tree_node *node_b, *node_c; - node_b = new radix_tree_node(val); + node_b = new radix_tree_node; - node_b->m_parent = node_a; - node_b->m_key = nul; - node_b->m_depth = node_a->m_depth + count; - node_b->m_is_leaf = true; - node_b->m_parent->m_children[nul] = node_b; - - return node_b; - } else { - radix_tree_node *node_b, *node_c; + node_b->m_parent = node_a; + node_b->m_depth = node->m_depth; + node_b->m_key = radix_substr(val.first, node_b->m_depth, len2 - count); + node_b->m_parent->m_children[node_b->m_key] = node_b; - node_b = new radix_tree_node; + node_c = new radix_tree_node(val); - node_b->m_parent = node_a; - node_b->m_depth = node->m_depth; - node_b->m_key = radix_substr(val.first, - node_b->m_depth, len2 - count); - node_b->m_parent->m_children[node_b->m_key] = node_b; + node_c->m_parent = node_b; + node_c->m_depth = radix_length(val.first); + node_c->m_key = nul; + node_c->m_is_leaf = true; + node_c->m_parent->m_children[nul] = node_c; - - node_c = new radix_tree_node(val); - - node_c->m_parent = node_b; - node_c->m_depth = radix_length(val.first); - node_c->m_key = nul; - node_c->m_is_leaf = true; - node_c->m_parent->m_children[nul] = node_c; - - return node_c; - } + return node_c; + } } template -std::pair::iterator, bool> -radix_tree::insert(const value_type &val) +std::pair::iterator, bool> radix_tree::insert(const value_type &val) { - if (m_root == NULL) { - K nul = radix_substr(val.first, 0, 0); + if (m_root == NULL) { + K nul = radix_substr(val.first, 0, 0); - m_root = new radix_tree_node; - m_root->m_key = nul; - } + m_root = new radix_tree_node; + m_root->m_key = nul; + } - m_size++; - radix_tree_node *node = find_node(val.first, m_root, 0); + radix_tree_node *node = find_node(val.first, m_root, 0); - if (node->m_is_leaf) { - node->m_value->second = val.second; - return std::pair(node, true); - } else if (node == m_root) { - return std::pair(append(m_root, val), true); - } else { - int len = radix_length(node->m_key); - K key_sub = radix_substr(val.first, node->m_depth, len); + if (node->m_is_leaf) { + return std::pair(node, false); + } else if (node == m_root) { + m_size++; + return std::pair(append(m_root, val), true); + } else { + m_size++; + int len = radix_length(node->m_key); + K key_sub = radix_substr(val.first, node->m_depth, len); - if (key_sub == node->m_key) { - return std::pair(append(node, val), - true); - } else { - return std::pair(prepend(node, val), - true); - } + if (key_sub == node->m_key) { + return std::pair(append(node, val), true); + } else { + return std::pair(prepend(node, val), true); } + } } template -typename radix_tree::iterator -radix_tree::find(const K &key) +typename radix_tree::iterator radix_tree::find(const K &key) { - if (m_root == NULL) - return iterator(NULL); + if (m_root == NULL) + return iterator(NULL); - radix_tree_node *node = find_node(key, m_root, 0); + radix_tree_node *node = find_node(key, m_root, 0); - // if the node is a internal node, return NULL - if (! node->m_is_leaf) - return iterator(NULL); + // if the node is a internal node, return NULL + if (! node->m_is_leaf) + return iterator(NULL); - return iterator(node); + return iterator(node); } template -radix_tree_node* -radix_tree::find_node(const K &key, radix_tree_node *node, - int depth) +radix_tree_node* radix_tree::find_node(const K &key, radix_tree_node *node, int depth) { - for (;;) { - cont: - if (node->m_children.size() == 0) - return node; - - typename radix_tree_node::it_child it; - int len_key = radix_length(key) - depth; - - for (it = node->m_children.begin(); - it != node->m_children.end(); ++it) { - if (len_key == 0) { - if (it->second->m_is_leaf) - return it->second; - else - continue; - } - - if (! it->second->m_is_leaf && - key[depth] == it->first[0]) { - int len_node = radix_length(it->first); - K key_sub = radix_substr(key, depth, - len_node); - - if (key_sub == it->first) { - node = it->second; - depth += len_node; - goto cont; - } else { - return it->second; - } - } + for (;;) { +cont: + if (node->m_children.empty()) + return node; + + typename radix_tree_node::it_child it; + int len_key = radix_length(key) - depth; + + for (it = node->m_children.begin(); it != node->m_children.end(); ++it) { + if (len_key == 0) { + if (it->second->m_is_leaf) + return it->second; + else + continue; + } + + if (! it->second->m_is_leaf && key[depth] == it->first[0] ) { + int len_node = radix_length(it->first); + K key_sub = radix_substr(key, depth, len_node); + + if (key_sub == it->first) { + node = it->second; + depth += len_node; + goto cont; + } else { + return it->second; } - - return node; + } } + + return node; + } } /* diff --git a/include/radix_tree/radix_tree_it.hpp b/include/radix_tree/radix_tree_it.hpp index 8b8e78d..9e7f869 100644 --- a/include/radix_tree/radix_tree_it.hpp +++ b/include/radix_tree/radix_tree_it.hpp @@ -8,128 +8,107 @@ template class radix_tree; template class radix_tree_node; template -class radix_tree_it : public std::iterator > { - friend class radix_tree; +class radix_tree_it : public std::iterator > { + friend class radix_tree; public: - radix_tree_it() : m_pointee(0) { } - ~radix_tree_it() { } + radix_tree_it() : m_pointee(0) { } + ~radix_tree_it() { } - std::pair& operator* () const; - std::pair* operator-> () const; - const radix_tree_it& operator++ (); - radix_tree_it operator++ (int); - // const radix_tree_it& operator-- (); - bool operator!= (const radix_tree_it &lhs) const; - bool operator== (const radix_tree_it &lhs) const; + std::pair& operator* () const; + std::pair* operator-> () const; + const radix_tree_it& operator++ (); + radix_tree_it operator++ (int); + // const radix_tree_it& operator-- (); + bool operator!= (const radix_tree_it &lhs) const; + bool operator== (const radix_tree_it &lhs) const; private: - radix_tree_node *m_pointee; - radix_tree_it(radix_tree_node *p) : m_pointee(p) { } + radix_tree_node *m_pointee; + radix_tree_it(radix_tree_node *p) : m_pointee(p) { } - radix_tree_node* increment(radix_tree_node* node); - radix_tree_node* descend(radix_tree_node* node); + radix_tree_node* increment(radix_tree_node* node) const; + radix_tree_node* descend(radix_tree_node* node) const; }; template -radix_tree_node* -radix_tree_it::increment(radix_tree_node* node) +radix_tree_node* radix_tree_it::increment(radix_tree_node* node) const { - radix_tree_node* parent; + radix_tree_node* parent = node->m_parent; - parent = node->m_parent; + if (parent == NULL) + return NULL; - if (parent == NULL) - return NULL; + typename radix_tree_node::it_child it = parent->m_children.find(node->m_key); + assert(it != parent->m_children.end()); + ++it; - typename radix_tree_node::it_child it; - - it = parent->m_children.find(node->m_key); - ++it; - - if (it == parent->m_children.end()) - return increment(parent); - else - return descend(it->second); + if (it == parent->m_children.end()) + return increment(parent); + else + return descend(it->second); } template -radix_tree_node* -radix_tree_it::descend(radix_tree_node* node) +radix_tree_node* radix_tree_it::descend(radix_tree_node* node) const { - if (node->m_is_leaf) - return node; + if (node->m_is_leaf) + return node; - typename radix_tree_node::it_child it; + typename radix_tree_node::it_child it = node->m_children.begin(); - it = node->m_children.begin(); + assert(it != node->m_children.end()); - assert(it != node->m_children.end()); - - return descend(it->second); + return descend(it->second); } template -std::pair& -radix_tree_it::operator* () const +std::pair& radix_tree_it::operator* () const { - return *m_pointee->m_value; + return *m_pointee->m_value; } template -std::pair* -radix_tree_it::operator-> () const +std::pair* radix_tree_it::operator-> () const { - return m_pointee->m_value; + return m_pointee->m_value; } template -bool -radix_tree_it::operator!= (const radix_tree_it &lhs) const +bool radix_tree_it::operator!= (const radix_tree_it &lhs) const { - return m_pointee != lhs.m_pointee; + return m_pointee != lhs.m_pointee; } template -bool -radix_tree_it::operator== (const radix_tree_it &lhs) const +bool radix_tree_it::operator== (const radix_tree_it &lhs) const { - return m_pointee == lhs.m_pointee; + return m_pointee == lhs.m_pointee; } template -const radix_tree_it& -radix_tree_it::operator++ () +const radix_tree_it& radix_tree_it::operator++ () { - assert(m_pointee != NULL); - + if (m_pointee != NULL) // it is undefined behaviour to dereference iterator that is out of bounds... m_pointee = increment(m_pointee); - - return *this; + return *this; } template -radix_tree_it -radix_tree_it::operator++ (int) +radix_tree_it radix_tree_it::operator++ (int) { - assert(m_pointee != NULL); - - radix_tree_node *node = m_pointee; - - m_pointee = increment(m_pointee); - - return radix_tree_it(node); + radix_tree_it copy(*this); + ++(*this); + return copy; } /* template -const radix_tree_it& -radix_tree_it::operator-- () +const radix_tree_it& radix_tree_it::operator-- () { - assert(m_pointee != NULL); + assert(m_pointee != NULL); - return *this; + return *this; } */ diff --git a/include/radix_tree/radix_tree_node.hpp b/include/radix_tree/radix_tree_node.hpp index 9ac304b..f2583a2 100644 --- a/include/radix_tree/radix_tree_node.hpp +++ b/include/radix_tree/radix_tree_node.hpp @@ -5,45 +5,48 @@ template class radix_tree_node { - friend class radix_tree; - friend class radix_tree_it; + friend class radix_tree; + friend class radix_tree_it; - typedef std::pair value_type; - typedef typename std::map* >::iterator it_child; + typedef std::pair value_type; + typedef typename std::map* >::iterator it_child; private: - radix_tree_node() : m_parent(NULL), m_value(NULL), m_depth(0), - m_is_leaf(false) { } - radix_tree_node(const value_type &val); - - ~radix_tree_node(); - - std::map*> m_children; - radix_tree_node *m_parent; - value_type *m_value; - int m_depth; - bool m_is_leaf; - K m_key; + radix_tree_node() : m_children(), m_parent(NULL), m_value(NULL), m_depth(0), m_is_leaf(false), m_key() { } + radix_tree_node(const value_type &val); + radix_tree_node(const radix_tree_node&); // delete + radix_tree_node& operator=(const radix_tree_node&); // delete + + ~radix_tree_node(); + + std::map*> m_children; + radix_tree_node *m_parent; + value_type *m_value; + int m_depth; + bool m_is_leaf; + K m_key; }; template -radix_tree_node::radix_tree_node(const value_type &val) : m_parent(NULL), - m_depth(0), - m_is_leaf(false) +radix_tree_node::radix_tree_node(const value_type &val) : + m_children(), + m_parent(NULL), + m_value(NULL), + m_depth(0), + m_is_leaf(false), + m_key() { - m_value = new value_type(val); + m_value = new value_type(val); } template radix_tree_node::~radix_tree_node() { - it_child it; - - for (it = m_children.begin(); it != m_children.end(); ++it) { - delete it->second; - } - - delete m_value; + it_child it; + for (it = m_children.begin(); it != m_children.end(); ++it) { + delete it->second; + } + delete m_value; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cea7c78..cb80321 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,8 @@ endmacro() include_directories(${CMAKE_SOURCE_DIR}/include ${GTEST_INCLUDE_DIR}) +add_subdirectory(radix_tree) + cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi") cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi") cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi") diff --git a/tests/radix_tree/CMakeLists.txt b/tests/radix_tree/CMakeLists.txt new file mode 100644 index 0000000..f3c2bdc --- /dev/null +++ b/tests/radix_tree/CMakeLists.txt @@ -0,0 +1,9 @@ +include_directories(${CMAKE_SOURCE_DIR}/include/radix_tree) + +cxx_test("radix_tree::insert" test_radix_tree_insert "test_radix_tree_insert.cpp" "") +cxx_test("radix_tree::erase" test_radix_tree_erase "test_radix_tree_erase.cpp" "") +cxx_test("radix_tree::find" test_radix_tree_find "test_radix_tree_find.cpp" "") +cxx_test("radix_tree::prefix_match" test_radix_tree_prefix_match "test_radix_tree_prefix_match.cpp" "") +cxx_test("radix_tree::longest_match" test_radix_tree_longest_match "test_radix_tree_longest_match.cpp" "") +cxx_test("radix_tree::greedy_match" test_radix_tree_greedy_match "test_radix_tree_greedy_match.cpp" "") +cxx_test("radix_tree_iterator" test_radix_tree_iterator "test_radix_tree_iterator.cpp" "") diff --git a/tests/radix_tree/common.hpp b/tests/radix_tree/common.hpp new file mode 100644 index 0000000..a94e158 --- /dev/null +++ b/tests/radix_tree/common.hpp @@ -0,0 +1,43 @@ +#include +#include + +#include +#include + +// this file contains some common code for all tests to reduce the number of copypaste lines + +typedef radix_tree tree_t; +typedef std::vector vector_found_t; +typedef std::map map_found_t; + +template +bool is_unique(_RAIter begin, _RAIter end) { + std::sort(begin, end); + _RAIter it = std::adjacent_find(begin, end); + return it == end; +} + +template< typename T, size_t N > +std::vector make_vector( const T (&data)[N] ) +{ + return std::vector(data, data+N); +} + +std::vector get_unique_keys() { + const std::string unique_keys_strings[] = { + "a", "b", "ab", "ba", "aa", "bb", + "aaa", "aab", "aba", "baa", "bba", "bab", "abb", "bbb" + }; + std::vector vec = make_vector(unique_keys_strings); + EXPECT_TRUE( is_unique(vec.begin(), vec.end()) ); + return vec; +} + +map_found_t vec_found_to_map(const vector_found_t& vec) { + map_found_t result; + for (size_t i = 0; i < vec.size(); i++) { + tree_t::iterator it = vec[i]; + result[it->first] = it->second; + } + return result; +} diff --git a/tests/radix_tree/test_radix_tree_erase.cpp b/tests/radix_tree/test_radix_tree_erase.cpp new file mode 100644 index 0000000..d00e132 --- /dev/null +++ b/tests/radix_tree/test_radix_tree_erase.cpp @@ -0,0 +1,130 @@ +#include "common.hpp" + +TEST(erase, change_size) +{ + std::vector unique_keys = get_unique_keys(); + for (size_t i = 0; i < unique_keys.size(); i++) { + tree_t tree; + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + std::pair r = tree.insert( tree_t::value_type(key, rand()%100) ); + } + } + { + SCOPED_TRACE("try to erase every key"); + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + size_t size_before_erase = tree.size(); + tree.erase(key); + ASSERT_EQ(size_before_erase - 1, tree.size()); + } + ASSERT_EQ(0u, tree.size()); + } + } +} + +TEST(erase, success_if_key_exist_fail_if_no_such_key) +{ + std::vector unique_keys = get_unique_keys(); + for (size_t i = 0; i < unique_keys.size(); i++) { + tree_t tree; + { + SCOPED_TRACE("try to erase keys never inserted before"); + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + bool erased = tree.erase(key); + ASSERT_FALSE(erased); + } + } + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + tree.insert( tree_t::value_type(key, rand()%100) ); + } + } + { + SCOPED_TRACE("try to erase existent key"); + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + bool erased = tree.erase(key); + ASSERT_TRUE(erased); + } + } + { + SCOPED_TRACE("try to erase already removed key"); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + bool erased = tree.erase(key); + ASSERT_FALSE(erased); + } + } + } +} + +TEST(erase, not_greedy) +{ + tree_t tree; + tree["bro"] = 1; + tree["brother"] = 2; + + { + SCOPED_TRACE("before erase"); + vector_found_t vec; + tree.prefix_match("bro", vec); + map_found_t should_be_found; + should_be_found["bro"] = 1; + should_be_found["brother"] = 2; + ASSERT_EQ(should_be_found, vec_found_to_map(vec)); + } + { + SCOPED_TRACE("after erase"); + tree.erase("bro"); + vector_found_t vec; + tree.prefix_match("bro", vec); + map_found_t should_be_found; + should_be_found["brother"] = 2; + ASSERT_EQ(should_be_found, vec_found_to_map(vec)); + } +} + +TEST(erase, empty_key) +{ + { + SCOPED_TRACE("tree contains only empty key"); + tree_t tree; + tree[""] = 1; + bool erased = tree.erase(""); + ASSERT_TRUE(erased); + ASSERT_EQ(tree.end(), tree.find("")); + } + { + SCOPED_TRACE("tree contains not only empty key"); + std::vector unique_keys = get_unique_keys(); + tree_t tree; + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + std::pair r = tree.insert( tree_t::value_type(key, rand()%100) ); + } + } + + tree[""] = 1; + bool erased = tree.erase(""); + ASSERT_TRUE(erased); + ASSERT_EQ(tree.end(), tree.find("")); + + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + ASSERT_NE(tree.end(), tree.find(key)); + bool key_erased = tree.erase(key); + ASSERT_TRUE(key_erased); + } + } +} diff --git a/tests/radix_tree/test_radix_tree_find.cpp b/tests/radix_tree/test_radix_tree_find.cpp new file mode 100644 index 0000000..fb1dddb --- /dev/null +++ b/tests/radix_tree/test_radix_tree_find.cpp @@ -0,0 +1,49 @@ +#include "common.hpp" + +TEST(find, nothing_in_empty) +{ + std::vector unique_keys = get_unique_keys(); + tree_t tree; + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + tree_t::iterator it = tree.find(key); + ASSERT_EQ(tree.end(), it); + } +} + +TEST(find, find_after_insert_or_erase) +{ + std::vector unique_keys = get_unique_keys(); + for (size_t i = 0; i < unique_keys.size(); i++) { + tree_t tree; + std::map value_map; + { // fill tree with some data and save values in map + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + int value = rand()%100; + std::pair r = tree.insert( tree_t::value_type(key, value) ); + value_map[key]=value; + } + } + { + SCOPED_TRACE("try to find existent key"); + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + tree_t::iterator it = tree.find(key); + ASSERT_NE(tree.end(), it); + ASSERT_EQ(value_map[it->first], it->second); + } + } + { + SCOPED_TRACE("find should fail to find removed key"); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + tree.erase(key); + tree_t::iterator it = tree.find(key); + ASSERT_EQ(tree.end(), it); + } + } + } +} diff --git a/tests/radix_tree/test_radix_tree_greedy_match.cpp b/tests/radix_tree/test_radix_tree_greedy_match.cpp new file mode 100644 index 0000000..464dc40 --- /dev/null +++ b/tests/radix_tree/test_radix_tree_greedy_match.cpp @@ -0,0 +1,80 @@ +#include "common.hpp" + +TEST(greedy_match, complex_tree) +{ + tree_t tree; + + tree["apache"] = 0; + tree["afford"] = 1; + tree["available"] = 2; + tree["affair"] = 3; + tree["avenger"] = 4; + tree["binary"] = 5; + tree["bind"] = 6; + tree["brother"] = 7; + tree["brace"] = 8; + tree["blind"] = 9; + tree["bro"] = 10; + + { + SCOPED_TRACE("greedy_match should find at least 1 object by existent key"); + for (tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + vector_found_t vec; + tree.greedy_match(it->first, vec); + ASSERT_GE(vec.size(), 1u); + map_found_t map_found = vec_found_to_map(vec); + ASSERT_NE(map_found.end(), map_found.find(it->first)) << "there is no such key in found"; + ASSERT_EQ(map_found[it->first], it->second); + } + } + { + SCOPED_TRACE("greedy_match corrects typos"); + typedef std::map typos_t; + typos_t typos; + { + // build typos START + const std::string typos_strings[] = { + // format: "typo,FOUND_1,FOUND_2,FOUND_N" + // if FOUND == * then FOUND treated as all keys in tree + "apple,apache", + "zzzzz,*", + ",*", + "lalalalala,*", + "avoid,available,avenger", + "bring,brace,bro,brother", + "biss,binary,bind", + "attack,affair,afford,apache,available,avenger" + }; + std::vector typos_vector = make_vector(typos_strings); + for (size_t i = 0; i < typos_vector.size(); i++) { + std::vector elems; + std::stringstream ss(typos_vector[i]); + std::string item; + while (std::getline(ss, item, ',')) { + elems.push_back(item); + item.clear(); + } + map_found_t map_found; + if (elems.size() == 2 && elems[1] == "*") { + for (tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + map_found[it->first] = it->second; + } + } else { + for (size_t j = 1 /* skip the first elem*/; j < elems.size(); j++) { + const std::string key = elems[j]; + map_found[key] = tree[key]; + } + } + typos[ elems[0] ] = map_found; + } + // build typos END + } + + for (typos_t::const_iterator typo = typos.begin(); typo != typos.end(); ++typo) { + SCOPED_TRACE(typo->first); + vector_found_t vec; + tree.greedy_match(typo->first, vec); + ASSERT_EQ(typo->second, vec_found_to_map(vec)); + } + } +} diff --git a/tests/radix_tree/test_radix_tree_insert.cpp b/tests/radix_tree/test_radix_tree_insert.cpp new file mode 100644 index 0000000..ffc1762 --- /dev/null +++ b/tests/radix_tree/test_radix_tree_insert.cpp @@ -0,0 +1,120 @@ +#include "common.hpp" + +TEST(insert, change_size) +{ + std::vector unique_keys = get_unique_keys(); + for (size_t i = 0; i < unique_keys.size(); i++) { + tree_t tree; + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + ASSERT_EQ(j, tree.size()); + tree.insert( tree_t::value_type(key, rand()%100) ); + ASSERT_EQ(j+1, tree.size()); + } + } + { // try to insert with duplicate keys + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + ASSERT_EQ(unique_keys.size(), tree.size()); + tree.insert( tree_t::value_type(key, rand()%100) ); + ASSERT_EQ(unique_keys.size(), tree.size()); + } + } + } +} + +TEST(insert, success_if_key_unique) +{ + std::vector unique_keys = get_unique_keys(); + for (size_t i = 0; i < unique_keys.size(); i++) { + tree_t tree; + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + std::pair r = tree.insert( tree_t::value_type(key, rand()%100) ); + ASSERT_TRUE( r.second ) << "fail to insert key=" << key; + } + } +} + +TEST(insert, fail_if_key_duplicate) +{ + std::vector unique_keys = get_unique_keys(); + for (size_t i = 0; i < unique_keys.size(); i++) { + tree_t tree; + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + tree.insert( tree_t::value_type(key, rand()%100) ); + } + } + { // try to insert with duplicate keys + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + std::pair r = tree.insert( tree_t::value_type(key, rand()%100) ); + ASSERT_FALSE( r.second ) << "unexpectedly inserted key=" << key; + } + } + } +} + +TEST(insert, dont_replace_prev_value) +{ + std::vector unique_keys = get_unique_keys(); + for (size_t i = 0; i < unique_keys.size(); i++) { + tree_t tree; + std::map value_map; + { // fill tree with some data and save values in map + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + int value = rand()%100; + tree.insert( tree_t::value_type(key, value) ); + value_map[key]=value; + } + } + { // try to overwrite by key + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t j = 0; j < unique_keys.size(); j++) { + const std::string key = unique_keys[j]; + tree.insert( tree_t::value_type(key, rand()%100) );\ + } + } + { // check old data was not modified + ASSERT_EQ(value_map.size(), tree.size()); + for (tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + ASSERT_FALSE(value_map.find(it->first) == value_map.end()) << "there is no such key in map"; + ASSERT_EQ(value_map[it->first], it->second); + } + } + } +} + +TEST(insert, operator_index_call_default_ctor) +{ + std::vector unique_keys = get_unique_keys(); + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + tree_t tree; + for (size_t i = 0; i < unique_keys.size(); i++) { + ASSERT_EQ(0, tree[unique_keys[i]]); + } +} + +TEST(insert, after_erase) +{ + std::vector unique_keys = get_unique_keys(); + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + tree_t tree; + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + tree.insert( tree_t::value_type(key, rand()%100) ); + tree.erase(key); + std::pair r = tree.insert( tree_t::value_type(key, rand()%100) ); + ASSERT_TRUE(r.second); + } +} diff --git a/tests/radix_tree/test_radix_tree_iterator.cpp b/tests/radix_tree/test_radix_tree_iterator.cpp new file mode 100644 index 0000000..02f5fb0 --- /dev/null +++ b/tests/radix_tree/test_radix_tree_iterator.cpp @@ -0,0 +1,110 @@ +#include "common.hpp" + +TEST(iterator, begin_end) +{ + { + SCOPED_TRACE("empty tree"); + tree_t tree; + ASSERT_EQ(tree.begin(), tree.end()); + } + { + SCOPED_TRACE("non empty tree"); + tree_t tree; + std::vector unique_keys = get_unique_keys(); + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + tree.insert( tree_t::value_type(key, rand()%100) ); + } + } + ASSERT_NE(tree.begin(), tree.end()); + } +} + +TEST(iterator, distance) +{ + { + SCOPED_TRACE("empty tree"); + tree_t tree; + ASSERT_EQ(0, std::distance(tree.begin(), tree.end()) ); + } + { + SCOPED_TRACE("non empty tree"); + tree_t tree; + std::vector unique_keys = get_unique_keys(); + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + tree.insert( tree_t::value_type(key, rand()%100) ); + } + } + ASSERT_EQ(unique_keys.size(), size_t(std::distance(tree.begin(), tree.end())) ); + } +} + +TEST(iterator, increment) +{ + { + SCOPED_TRACE("empty tree"); + tree_t tree; + tree_t::iterator it = tree.begin(); + ASSERT_EQ(tree.begin(), it); + ASSERT_EQ(tree.end(), it); + ASSERT_NO_THROW( it++ ); + ASSERT_NO_THROW( ++it ); + } + { + SCOPED_TRACE("non empty tree"); + tree_t tree; + std::vector unique_keys = get_unique_keys(); + std::set keys; + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + tree.insert( tree_t::value_type(key, rand()%100) ); + keys.insert(key); + } + } + { + SCOPED_TRACE("postincrement"); + for(tree_t::iterator it = tree.begin(); it != tree.end(); ) { + ASSERT_NE(keys.end(), keys.find(it->first)); + tree_t::iterator copy = it++; + ASSERT_NE(copy, it); + } + } + { + SCOPED_TRACE("preincrement"); + for(tree_t::iterator it = tree.begin(); it != tree.end(); ) { + ASSERT_NE(keys.end(), keys.find(it->first)); + ASSERT_NE(keys.end(), keys.find((*it).first)); + tree_t::iterator copy = ++it; + ASSERT_EQ(copy, it); + } + } + } +} + +TEST(iterator, std__copy) +{ + SCOPED_TRACE("non empty tree"); + tree_t tree; + std::vector unique_keys = get_unique_keys(); + { // fill tree with some data + std::random_shuffle(unique_keys.begin(), unique_keys.end()); + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + tree.insert( tree_t::value_type(key, rand()%100) ); + } + } + std::map map; + std::copy(tree.begin(), tree.end(), std::inserter(map, map.end())); + + ASSERT_EQ(map.size(), size_t(std::distance(tree.begin(), tree.end())) ); + for(tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + ASSERT_NE(map.end(), map.find(it->first)); + } +} diff --git a/tests/radix_tree/test_radix_tree_longest_match.cpp b/tests/radix_tree/test_radix_tree_longest_match.cpp new file mode 100644 index 0000000..1e4b040 --- /dev/null +++ b/tests/radix_tree/test_radix_tree_longest_match.cpp @@ -0,0 +1,71 @@ +#include "common.hpp" + +TEST(longest_match, empty_tree) +{ + std::vector unique_keys = get_unique_keys(); + tree_t tree; + for (size_t i = 0; i < unique_keys.size(); i++) { + const std::string key = unique_keys[i]; + tree_t::iterator found_it = tree.longest_match(key); + ASSERT_EQ(found_it, tree.end()); + } +} + +TEST(longest_match, complex_tree) +{ + tree_t tree; + + tree["abcdef"] = 1; + tree["abcdege"] = 2; + tree["bcdef"] = 3; + tree["cd"] = 4; + tree["ce"] = 5; + tree["c"] = 6; + + { + SCOPED_TRACE("longest_match should find object by existent key"); + for (tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + tree_t::iterator found_it = tree.longest_match(it->first); + ASSERT_NE(found_it, tree.end()); + ASSERT_EQ(tree[it->first], found_it->second); + } + } + { + SCOPED_TRACE("longest_match should find nothing by empty key"); + tree_t::iterator found_it = tree.longest_match(""); + ASSERT_EQ(found_it, tree.end()); + } + { + SCOPED_TRACE("longest_match corrects typos in suffix"); + typedef std::map typos_t; + typos_t typos; + typos["abcdefe"] = "abcdef"; + typos["abcdegeasdf"] = "abcdege"; + typos["bcdefege"] = "bcdef"; + typos["ced"] = "ce"; + typos["cdef"] = "cd"; + typos["cf"] = "c"; + typos["ca"] = "c"; + typos["ccdef"] = "c"; + for (typos_t::const_iterator typo = typos.begin(); typo != typos.end(); ++typo) { + SCOPED_TRACE(typo->first); + tree_t::iterator found_it = tree.longest_match(typo->first); + ASSERT_NE(found_it, tree.end()); + ASSERT_EQ(typo->second, found_it->first); + ASSERT_EQ(tree[typo->second] , found_it->second); + } + } + { + SCOPED_TRACE("should never be found"); + const std::string never_found_strings[] = { + "a", "b", "d", "e", "f", "abcde", "bcdege", "acd", "bce", "acdef" + }; + std::vector should_never_be_found = make_vector(never_found_strings); + for (size_t i = 0; i < should_never_be_found.size(); i++) { + const std::string key = should_never_be_found[i]; + SCOPED_TRACE(key); + tree_t::iterator found_it = tree.longest_match(key); + ASSERT_EQ(found_it, tree.end()); + } + } +} diff --git a/tests/radix_tree/test_radix_tree_prefix_match.cpp b/tests/radix_tree/test_radix_tree_prefix_match.cpp new file mode 100644 index 0000000..ebfdd9b --- /dev/null +++ b/tests/radix_tree/test_radix_tree_prefix_match.cpp @@ -0,0 +1,96 @@ +#include "common.hpp" + +bool is_prefix_of(const std::string& prefix, const std::string& str) { + std::pair p = std::mismatch(prefix.begin(), prefix.end(), str.begin()); + return p.first == prefix.end(); +} + +void check_nonexistent_prefixes(tree_t& tree) +{ + SCOPED_TRACE("should never be found"); + const std::string never_found_strings[] = { + "abcdfe", "abcdefe", "abe", "cc", "abcdec", "bcdefc" + }; + std::vector should_never_be_found = make_vector(never_found_strings); + for (size_t i = 0; i < should_never_be_found.size(); i++) { + const std::string key = should_never_be_found[i]; + SCOPED_TRACE(key); + vector_found_t vec; + tree.prefix_match(key, vec); + ASSERT_EQ(0u, vec.size()); + } +} + +TEST(prefix_match, empty_tree) +{ + tree_t tree; + check_nonexistent_prefixes(tree); +} + +TEST(prefix_match, complex_tree) +{ + tree_t tree; + + tree["abcdef"] = 1; + tree["abcdege"] = 2; + tree["bcdef"] = 3; + tree["cd"] = 4; + tree["ce"] = 5; + tree["c"] = 6; + tree[""] = 7; + + { + SCOPED_TRACE("prefix_match should find at least 1 object by existent key"); + for (tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + vector_found_t vec; + tree.prefix_match(it->first, vec); + ASSERT_GE(vec.size(), 1u); + map_found_t map_found = vec_found_to_map(vec); + ASSERT_NE(map_found.end(), map_found.find(it->first)) << "there is no such key in found"; + ASSERT_EQ(map_found[it->first], it->second); + } + } + { + SCOPED_TRACE("prefix_match should find every object by empty key"); + vector_found_t vec; + tree.prefix_match("", vec); + map_found_t should_be_found; + for (tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + should_be_found[it->first] = it->second; + } + ASSERT_EQ(should_be_found, vec_found_to_map(vec)); + } + { + typedef std::map prefixes_t; + prefixes_t prefixes; + { + // build prefixes START + // iterate over each key in tree, make prefixes from it: prefix = key[0..N], N <- [0 .. lenght key] + for (tree_t::iterator it = tree.begin(); it != tree.end(); ++it) { + const std::string key = it->first; + for (size_t i = 0; i < key.size(); i++) { + const std::string prefix = key.substr(0, i); + + if (prefixes.find(prefix) != prefixes.end()) + continue; // we should not build prefixes if we have done it before + + vector_found_t vec; + for (tree_t::iterator each_it = tree.begin(); each_it != tree.end(); ++each_it) { + if ( is_prefix_of(prefix, each_it->first) ) + vec.push_back(each_it); + } + prefixes[prefix] = vec_found_to_map(vec); + } + } + // build prefixes END + } + + for (prefixes_t::const_iterator prefix_it = prefixes.begin(); prefix_it != prefixes.end(); ++prefix_it) { + SCOPED_TRACE(prefix_it->first); + vector_found_t vec; + tree.prefix_match(prefix_it->first, vec); + ASSERT_EQ(prefix_it->second, vec_found_to_map(vec)); + } + } + check_nonexistent_prefixes(tree); +} From f81187613666673f7b375e0ee705821b46e3fead Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Jul 2015 03:10:23 +0300 Subject: [PATCH 257/290] Moves __assert_fail into a standalone file --- tests/helpers/AssertFail.h | 11 +++++++++++ tests/helpers/ControlGraph.h | 2 ++ tests/patterns/DecodeBytecode.h | 7 ------- 3 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 tests/helpers/AssertFail.h diff --git a/tests/helpers/AssertFail.h b/tests/helpers/AssertFail.h new file mode 100644 index 0000000..17261d2 --- /dev/null +++ b/tests/helpers/AssertFail.h @@ -0,0 +1,11 @@ +#ifndef LLST_ASSERT_FAIL_INCLUDED +#define LLST_ASSERT_FAIL_INCLUDED + +void __assert_fail(const char *__assertion, const char *__file, unsigned int __line, const char */*__function*/) +{ + std::stringstream ss; + ss << "Assertion '" << __assertion << "' failed in '" << __file << "' at: " << __line; + throw ss.str(); +} + +#endif diff --git a/tests/helpers/ControlGraph.h b/tests/helpers/ControlGraph.h index ee9825d..890b67d 100644 --- a/tests/helpers/ControlGraph.h +++ b/tests/helpers/ControlGraph.h @@ -7,6 +7,8 @@ #include +#include "AssertFail.h" + class H_LastInstIsTerminator: public st::BasicBlockVisitor { public: diff --git a/tests/patterns/DecodeBytecode.h b/tests/patterns/DecodeBytecode.h index 1434a4f..33ba39d 100644 --- a/tests/patterns/DecodeBytecode.h +++ b/tests/patterns/DecodeBytecode.h @@ -5,13 +5,6 @@ #include #include -void __assert_fail(const char *__assertion, const char *__file, unsigned int __line, const char */*__function*/) -{ - std::stringstream ss; - ss << "Assertion '" << __assertion << "' failed in '" << __file << "' at: " << __line; - throw ss.str(); -} - class P_DecodeBytecode : public ::testing::TestWithParam > { public: From c321c41ca6261c23a897b15ffe47cfce87a454e7 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Jul 2015 03:14:00 +0300 Subject: [PATCH 258/290] Adds tests on VM primitives --- tests/CMakeLists.txt | 1 + tests/data/VMPrimitives.image | Bin 0 -> 31442 bytes tests/helpers/VMImage.h | 51 ++++++++++++++++++++++++++++++++++ tests/patterns/InitVMImage.h | 29 +++++++++++++++++++ tests/vm_primitives.cpp | 48 ++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 tests/data/VMPrimitives.image create mode 100644 tests/helpers/VMImage.h create mode 100644 tests/patterns/InitVMImage.h create mode 100644 tests/vm_primitives.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cb80321..b242a2c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,3 +21,4 @@ cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi") # TODO cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi") cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set") +cxx_test("VM::primitives" test_vm_primitives "${CMAKE_CURRENT_SOURCE_DIR}/vm_primitives.cpp" "memory_managers;standart_set") diff --git a/tests/data/VMPrimitives.image b/tests/data/VMPrimitives.image new file mode 100644 index 0000000000000000000000000000000000000000..05517729d48a3e75ce6e3c7b6cf480eed8f6ba27 GIT binary patch literal 31442 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 O@1cXuu$0T&{(k{~B +#include + +class H_VMImage +{ + std::auto_ptr m_memoryManager; + std::auto_ptr m_smalltalkImage; +public: + H_VMImage(const std::string& imageName) { + std::auto_ptr memoryManager(new BakerMemoryManager()); + memoryManager->initializeHeap(1024*1024, 1024*1024); + std::auto_ptr smalltalkImage(new Image(memoryManager.get())); + smalltalkImage->loadImage(TESTS_DIR "./data/" + imageName + ".image"); + } + TObjectArray* newArray(std::size_t fields); + template void deleteObject(T* object); +private: + TObject* newOrdinaryObject(TClass* klass, std::size_t slotSize); +}; + +inline TObject* H_VMImage::newOrdinaryObject(TClass* klass, std::size_t slotSize) +{ + void* objectSlot = new char[ correctPadding(slotSize) ]; + + // Object size stored in the TSize field of any ordinary object contains + // number of pointers except for the first two fields + std::size_t fieldsCount = slotSize / sizeof(TObject*) - 2; + + TObject* instance = new (objectSlot) TObject(fieldsCount, klass); + + for (uint32_t index = 0; index < fieldsCount; index++) + instance->putField(index, globals.nilObject); + + return instance; +} + +inline TObjectArray* H_VMImage::newArray(std::size_t fields) +{ + return static_cast( newOrdinaryObject(globals.arrayClass, sizeof(TObjectArray) + fields * sizeof(TObject*)) ); +} + +template +inline void H_VMImage::deleteObject(T* object) { + char* mem = reinterpret_cast(object); + delete[] mem; +} + +#endif diff --git a/tests/patterns/InitVMImage.h b/tests/patterns/InitVMImage.h new file mode 100644 index 0000000..7206014 --- /dev/null +++ b/tests/patterns/InitVMImage.h @@ -0,0 +1,29 @@ +#ifndef LLST_PATTERN_INIT_VM_IMAGE_INCLUDED +#define LLST_PATTERN_INIT_VM_IMAGE_INCLUDED + +#include +#include "../helpers/VMImage.h" + +class P_InitVM_Image : public ::testing::TestWithParam +{ +protected: + H_VMImage* m_image; +public: + virtual ~P_InitVM_Image() {} + + virtual void SetUp() + { + bool is_parameterized_test = ::testing::UnitTest::GetInstance()->current_test_info()->value_param(); + if (!is_parameterized_test) { + // Use TEST_P instead of TEST_F ! + abort(); + } + ParamType image_name = GetParam(); + m_image = new H_VMImage(image_name); + } + virtual void TearDown() { + delete m_image; + } +}; + +#endif diff --git a/tests/vm_primitives.cpp b/tests/vm_primitives.cpp new file mode 100644 index 0000000..750bbe6 --- /dev/null +++ b/tests/vm_primitives.cpp @@ -0,0 +1,48 @@ +#include +#include "helpers/VMImage.h" +#include "patterns/InitVMImage.h" +#include +#include + +INSTANTIATE_TEST_CASE_P(_, P_InitVM_Image, ::testing::Values(std::string("VMPrimitives")) ); + +TEST_P(P_InitVM_Image, smallint) +{ + TObjectArray* args = m_image->newArray(2); + { + SCOPED_TRACE("1+2"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(2) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntAdd, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(3, result.getValue()); + } + { + SCOPED_TRACE("1/0"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::smallIntDiv, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + } + { + SCOPED_TRACE("8/4"); + args->putField(0, TInteger(8) ); + args->putField(1, TInteger(4) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntDiv, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(2, result.getValue()); + } + { + SCOPED_TRACE("2*3"); + args->putField(0, TInteger(2) ); + args->putField(1, TInteger(3) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntMul, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(6, result.getValue()); + } + m_image->deleteObject(args); +} From cd1b58c27595bb2695d80cd7abc0ea424106b194 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 4 Jul 2015 03:39:50 +0300 Subject: [PATCH 259/290] Improves tests on VM primitives --- tests/vm_primitives.cpp | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/tests/vm_primitives.cpp b/tests/vm_primitives.cpp index 750bbe6..80f6f90 100644 --- a/tests/vm_primitives.cpp +++ b/tests/vm_primitives.cpp @@ -18,6 +18,15 @@ TEST_P(P_InitVM_Image, smallint) ASSERT_FALSE(primitiveFailed); ASSERT_EQ(3, result.getValue()); } + { + SCOPED_TRACE("1-2"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(2) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntSub, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(-1, result.getValue()); + } { SCOPED_TRACE("1/0"); args->putField(0, TInteger(1) ); @@ -35,6 +44,23 @@ TEST_P(P_InitVM_Image, smallint) ASSERT_FALSE(primitiveFailed); ASSERT_EQ(2, result.getValue()); } + { + SCOPED_TRACE("1%0"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::smallIntMod, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + } + { + SCOPED_TRACE("3%2"); + args->putField(0, TInteger(3) ); + args->putField(1, TInteger(2) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntMod, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(1, result.getValue()); + } { SCOPED_TRACE("2*3"); args->putField(0, TInteger(2) ); @@ -44,5 +70,85 @@ TEST_P(P_InitVM_Image, smallint) ASSERT_FALSE(primitiveFailed); ASSERT_EQ(6, result.getValue()); } + { + SCOPED_TRACE("1<2"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(2) ); + bool primitiveFailed; + TObject* result = callPrimitive(primitive::smallIntLess, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(globals.trueObject, result); + } + { + SCOPED_TRACE("1<1"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(1) ); + bool primitiveFailed; + TObject* result = callPrimitive(primitive::smallIntLess, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(globals.falseObject, result); + } + { + SCOPED_TRACE("3=3"); + args->putField(0, TInteger(3) ); + args->putField(1, TInteger(3) ); + bool primitiveFailed; + TObject* result = callPrimitive(primitive::smallIntEqual, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(globals.trueObject, result); + } + { + SCOPED_TRACE("0=42"); + args->putField(0, TInteger(0) ); + args->putField(1, TInteger(42) ); + bool primitiveFailed; + TObject* result = callPrimitive(primitive::smallIntEqual, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(globals.falseObject, result); + } + { + SCOPED_TRACE("1|2"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(2) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntBitOr, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(3, result.getValue()); + } + { + SCOPED_TRACE("14&3"); + args->putField(0, TInteger(14) ); + args->putField(1, TInteger(3) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntBitAnd, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(2, result.getValue()); + } + { + SCOPED_TRACE("7>>1"); + args->putField(0, TInteger(7) ); + args->putField(1, TInteger(-1) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntBitShift, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(3, result.getValue()); + } + { + SCOPED_TRACE("5<<1"); + args->putField(0, TInteger(5) ); + args->putField(1, TInteger(1) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::smallIntBitShift, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(10, result.getValue()); + } + { + SCOPED_TRACE("1<<31"); + args->putField(0, TInteger(1) ); + args->putField(1, TInteger(31) ); + bool primitiveFailed; + callPrimitive(primitive::smallIntBitShift, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + } m_image->deleteObject(args); } From b2615ffc1db905469b03276f154876a32437f0e8 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 5 Jul 2015 13:26:27 +0300 Subject: [PATCH 260/290] Adds tests and fixes primitive::stringAt --- src/primitives.cpp | 2 +- tests/helpers/VMImage.h | 29 ++++++++++++++++ tests/vm_primitives.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/primitives.cpp b/src/primitives.cpp index 0364906..c858373 100644 --- a/src/primitives.cpp +++ b/src/primitives.cpp @@ -87,7 +87,7 @@ TObject* callPrimitive(uint8_t opcode, TObjectArray* arguments, bool& primitiveF //valueObject is not used in primitive stringAtPut } - if (! isSmallInteger(indexObject)) { + if (isSmallInteger(string) || string->getClass() != globals.stringClass || !isSmallInteger(indexObject)) { primitiveFailed = true; break; } diff --git a/tests/helpers/VMImage.h b/tests/helpers/VMImage.h index 3fb3850..26fb851 100644 --- a/tests/helpers/VMImage.h +++ b/tests/helpers/VMImage.h @@ -4,6 +4,8 @@ #include #include +#include + class H_VMImage { std::auto_ptr m_memoryManager; @@ -16,9 +18,12 @@ class H_VMImage smalltalkImage->loadImage(TESTS_DIR "./data/" + imageName + ".image"); } TObjectArray* newArray(std::size_t fields); + TString* newString(std::size_t size); + TString* newString(const std::string& str); template void deleteObject(T* object); private: TObject* newOrdinaryObject(TClass* klass, std::size_t slotSize); + TByteObject* newBinaryObject(TClass* klass, std::size_t dataSize); }; inline TObject* H_VMImage::newOrdinaryObject(TClass* klass, std::size_t slotSize) @@ -37,11 +42,35 @@ inline TObject* H_VMImage::newOrdinaryObject(TClass* klass, std::size_t slotSize return instance; } +inline TByteObject* H_VMImage::newBinaryObject(TClass* klass, std::size_t dataSize) +{ + // All binary objects are descendants of ByteObject + // They could not have ordinary fields, so we may use it + uint32_t slotSize = sizeof(TByteObject) + dataSize; + + void* objectSlot = new char[ correctPadding(slotSize) ]; + TByteObject* instance = new (objectSlot) TByteObject(dataSize, klass); + + return instance; +} + inline TObjectArray* H_VMImage::newArray(std::size_t fields) { return static_cast( newOrdinaryObject(globals.arrayClass, sizeof(TObjectArray) + fields * sizeof(TObject*)) ); } +inline TString* H_VMImage::newString(std::size_t size) +{ + return static_cast( newBinaryObject(globals.stringClass, size) ); +} + +inline TString* H_VMImage::newString(const std::string& str) +{ + TString* result = newString(str.size()); + std::memcpy(result->getBytes(), str.c_str(), str.size()); + return result; +} + template inline void H_VMImage::deleteObject(T* object) { char* mem = reinterpret_cast(object); diff --git a/tests/vm_primitives.cpp b/tests/vm_primitives.cpp index 80f6f90..ac90174 100644 --- a/tests/vm_primitives.cpp +++ b/tests/vm_primitives.cpp @@ -152,3 +152,78 @@ TEST_P(P_InitVM_Image, smallint) } m_image->deleteObject(args); } + +TEST_P(P_InitVM_Image, stringAt) +{ + { + SCOPED_TRACE("inbounds"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + for (std::size_t i = 0; i < sample.size(); i++) + { + SCOPED_TRACE(i); + TObjectArray* args = m_image->newArray(2); + args->putField(0, str ); + args->putField(1, TInteger(i+1) ); + bool primitiveFailed; + TInteger result = callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ(sample[i], result.getValue()); + m_image->deleteObject(args); + } + m_image->deleteObject(str); + } + { + SCOPED_TRACE("out of bounds"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(2); + args->putField(0, str ); + args->putField(1, TInteger(42) ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("string replaced with smallint"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(2); + args->putField(0, TInteger(0) ); + args->putField(1, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("string replaced with array"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(2); + args->putField(0, m_image->newArray(42) ); + args->putField(1, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args->getField(0)); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("index is not a smallint"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(2); + args->putField(0, TInteger(0) ); + args->putField(1, str ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args); + m_image->deleteObject(str); + } +} From 9b5beee626202760f2bc499f5481cc9e1e1761ce Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 5 Jul 2015 15:29:04 +0300 Subject: [PATCH 261/290] Tests on primitives stringAt and stringAtPut --- tests/vm_primitives.cpp | 72 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/tests/vm_primitives.cpp b/tests/vm_primitives.cpp index ac90174..4698f36 100644 --- a/tests/vm_primitives.cpp +++ b/tests/vm_primitives.cpp @@ -155,6 +155,46 @@ TEST_P(P_InitVM_Image, smallint) TEST_P(P_InitVM_Image, stringAt) { + { + SCOPED_TRACE("string replaced with smallint"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(2); + args->putField(0, TInteger(0) ); + args->putField(1, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("string replaced with array"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(2); + args->putField(0, m_image->newArray(42) ); + args->putField(1, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args->getField(0)); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("index is not a smallint"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(2); + args->putField(0, TInteger(0) ); + args->putField(1, str ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args); + m_image->deleteObject(str); + } { SCOPED_TRACE("inbounds"); std::string sample = "Hello world"; @@ -186,15 +226,20 @@ TEST_P(P_InitVM_Image, stringAt) m_image->deleteObject(args); m_image->deleteObject(str); } +} + +TEST_P(P_InitVM_Image, stringAtPut) +{ { SCOPED_TRACE("string replaced with smallint"); std::string sample = "Hello world"; TString* str = m_image->newString(sample); - TObjectArray* args = m_image->newArray(2); + TObjectArray* args = m_image->newArray(3); args->putField(0, TInteger(0) ); args->putField(1, TInteger(0) ); + args->putField(2, TInteger(0) ); bool primitiveFailed; - callPrimitive(primitive::stringAt, args, primitiveFailed); + callPrimitive(primitive::stringAtPut, args, primitiveFailed); ASSERT_TRUE(primitiveFailed); m_image->deleteObject(args); m_image->deleteObject(str); @@ -203,11 +248,12 @@ TEST_P(P_InitVM_Image, stringAt) SCOPED_TRACE("string replaced with array"); std::string sample = "Hello world"; TString* str = m_image->newString(sample); - TObjectArray* args = m_image->newArray(2); + TObjectArray* args = m_image->newArray(3); args->putField(0, m_image->newArray(42) ); args->putField(1, TInteger(0) ); + args->putField(2, TInteger(0) ); bool primitiveFailed; - callPrimitive(primitive::stringAt, args, primitiveFailed); + callPrimitive(primitive::stringAtPut, args, primitiveFailed); ASSERT_TRUE(primitiveFailed); m_image->deleteObject(args->getField(0)); m_image->deleteObject(args); @@ -217,12 +263,28 @@ TEST_P(P_InitVM_Image, stringAt) SCOPED_TRACE("index is not a smallint"); std::string sample = "Hello world"; TString* str = m_image->newString(sample); - TObjectArray* args = m_image->newArray(2); + TObjectArray* args = m_image->newArray(3); + args->putField(0, TInteger(0) ); + args->putField(1, str ); + args->putField(2, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::stringAt, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("value is not a smallint"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(3); args->putField(0, TInteger(0) ); args->putField(1, str ); + args->putField(2, m_image->newArray(42) ); bool primitiveFailed; callPrimitive(primitive::stringAt, args, primitiveFailed); ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args->getField(2)); m_image->deleteObject(args); m_image->deleteObject(str); } From cf7e92be1ef3e24b1a6f2bae00748d2ad19737ab Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 7 Jul 2015 14:05:26 +0300 Subject: [PATCH 262/290] Fixes tests on primitive::stringAtPut --- tests/vm_primitives.cpp | 47 +++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/tests/vm_primitives.cpp b/tests/vm_primitives.cpp index 4698f36..40092a5 100644 --- a/tests/vm_primitives.cpp +++ b/tests/vm_primitives.cpp @@ -249,13 +249,13 @@ TEST_P(P_InitVM_Image, stringAtPut) std::string sample = "Hello world"; TString* str = m_image->newString(sample); TObjectArray* args = m_image->newArray(3); - args->putField(0, m_image->newArray(42) ); - args->putField(1, TInteger(0) ); + args->putField(0, TInteger(0) ); + args->putField(1, m_image->newArray(42) ); args->putField(2, TInteger(0) ); bool primitiveFailed; callPrimitive(primitive::stringAtPut, args, primitiveFailed); ASSERT_TRUE(primitiveFailed); - m_image->deleteObject(args->getField(0)); + m_image->deleteObject(args->getField(1)); m_image->deleteObject(args); m_image->deleteObject(str); } @@ -266,9 +266,9 @@ TEST_P(P_InitVM_Image, stringAtPut) TObjectArray* args = m_image->newArray(3); args->putField(0, TInteger(0) ); args->putField(1, str ); - args->putField(2, TInteger(0) ); + args->putField(2, str ); bool primitiveFailed; - callPrimitive(primitive::stringAt, args, primitiveFailed); + callPrimitive(primitive::stringAtPut, args, primitiveFailed); ASSERT_TRUE(primitiveFailed); m_image->deleteObject(args); m_image->deleteObject(str); @@ -278,13 +278,42 @@ TEST_P(P_InitVM_Image, stringAtPut) std::string sample = "Hello world"; TString* str = m_image->newString(sample); TObjectArray* args = m_image->newArray(3); - args->putField(0, TInteger(0) ); + args->putField(0, m_image->newArray(42) ); args->putField(1, str ); - args->putField(2, m_image->newArray(42) ); + args->putField(2, TInteger(0) ); bool primitiveFailed; - callPrimitive(primitive::stringAt, args, primitiveFailed); + callPrimitive(primitive::stringAtPut, args, primitiveFailed); + ASSERT_TRUE(primitiveFailed); + m_image->deleteObject(args->getField(0)); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("out of bounds"); + std::string sample = "Hello world"; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(3); + args->putField(0, str ); + args->putField(1, TInteger(42) ); + args->putField(2, TInteger(0) ); + bool primitiveFailed; + callPrimitive(primitive::stringAtPut, args, primitiveFailed); ASSERT_TRUE(primitiveFailed); - m_image->deleteObject(args->getField(2)); + m_image->deleteObject(args); + m_image->deleteObject(str); + } + { + SCOPED_TRACE("inbounds"); + std::string sample = "Hello world "; + TString* str = m_image->newString(sample); + TObjectArray* args = m_image->newArray(3); + args->putField(0, TInteger('!') ); + args->putField(1, str ); + args->putField(2, TInteger(12) ); + bool primitiveFailed; + callPrimitive(primitive::stringAtPut, args, primitiveFailed); + ASSERT_FALSE(primitiveFailed); + ASSERT_EQ("Hello world!", std::string((const char*)str->getBytes(), str->getSize())); m_image->deleteObject(args); m_image->deleteObject(str); } From 35e0f81a5ce513b83be3d7c2f39b0de89bc7cfea Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 30 Jul 2015 01:34:32 +0300 Subject: [PATCH 263/290] Tries to fix some warnings in .travis.yml --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 62e82d9..9a381cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,9 @@ os: sudo: false cache: - - apt - - pip + apt: true + directories: + - $HOME/.cache/pip addons: apt: packages: @@ -41,7 +42,7 @@ env: - USE_LLVM=Off BUILD_TYPE=Coverage -install: +before_install: - pip install --user cpp-coveralls before_script: @@ -50,8 +51,8 @@ before_script: - cmake .. -DUSE_LLVM=$USE_LLVM -DBUILD_TESTS=On -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DSITE="${TRAVIS_OS_NAME}@Travis" -DBUILDNAME="${TRAVIS_BRANCH}_${CXX}_LLVM_${USE_LLVM}_${BUILD_TYPE}" script: - - ctest -M Experimental -T Start -T Build -T Test -T Submit - - cmake --build . -- all --keep-going && cmake --build . --target check # If ctest built the project unsuccessfully, `make all && make check` will report errors to Travis + - cmake --build . -- all --keep-going + - cmake --build . --target check after_success: - if [ ${CXX} == g++ ] && [ ${BUILD_TYPE} == Coverage ]; then ~/.local/bin/coveralls --repo_token "INxVunXhVXbQWjVbwoIisKeXSJqCRGnI2" --exclude build/tests/gtest -E ".*CMake.*CompilerId.c" --gcov-options "\-lp" -r ../; fi From 5ac9e85d994bf5d5d5919975ec56837999745f0a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 11 Jun 2015 01:46:53 +0300 Subject: [PATCH 264/290] Removes shell script which generates ChangeLog Issue: #82 --- misc/changelog.sh | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100755 misc/changelog.sh diff --git a/misc/changelog.sh b/misc/changelog.sh deleted file mode 100755 index aa5c52f..0000000 --- a/misc/changelog.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# Author:Andrey Nikishaev - -FORMAT="%ad %an <%ae>%n%n%w(0,4,4)%B" - -git tag -l | sort -u -r | while read TAG ; do - echo - if [ $NEXT ];then - echo [$NEXT] - else - echo "[Current]" - fi - GIT_PAGER=cat git log --no-merges --date=short --format="$FORMAT" $TAG..$NEXT - NEXT=$TAG -done -FIRST=$(git tag -l | head -1) -echo [$FIRST] -GIT_PAGER=cat git log --no-merges --date=short --format="$FORMAT" $FIRST From 2e75bb783cfc198905e64b7095adc4f7944610a1 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 11 Jun 2015 01:41:42 +0300 Subject: [PATCH 265/290] Clears ChangeLog Issue: #82 --- ChangeLog | 4017 ----------------------------------------------------- 1 file changed, 4017 deletions(-) diff --git a/ChangeLog b/ChangeLog index c22b408..e69de29 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4017 +0,0 @@ - -[Current] - -[v_0.3] -2015-01-30 Dmitry Kashitsyn - - Fixes Baker MM reallocation - - Issue: #14 - -2015-01-30 Roman Proskuryakov - - Moves to llvm 3.3 - - Issue: #49 - -2015-01-30 Roman Proskuryakov - - Fixes the deps of debian package. - -2014-03-25 Roman Proskuryakov - - The code generated page fault during execution of movdqa instruction - This SSE instruction requered the data to be alined by 16 bytes. - - The code corresponding to the bug is: - - @SmalltalkVM::newOrdinaryObject - for (uint32_t index = 0; index < fieldsCount; index++) - instance->putField(index, globals.nilObject); - - movdqa moves a double quadword from src to dst. The compiler optimized this loop into a set of movdqa instructions. - That means that 'instance' was aligned neither by 16 bytes nor by 4 bytes. - - When BakerMemoryManager::growHeap does its job it divides newHeapSize by 2. - newHeapSize/2 must be equal to correctPadding(newHeapSize/2). But it is not. - - Issue: #28 - -2014-05-14 Roman - - Updates README.md - - Issue: #50 -2014-05-14 Roman Proskuryakov - - Moves targets 'doc' and 'image' into their own CMakeLists.txt - - Issue: #50 - -2014-04-09 Roman Proskuryakov - - Adds FindPOD2MAN.cmake and FindGZIP.cmake - - Adds option 'BUILD_DOCS'. - Now cmake is able to convert pod files into man pages. - - Issue: #50 - -2014-05-09 Roman Proskuryakov - - Adds toolchain for mingw32 - - Issue: #50 - -2014-05-12 Roman Proskuryakov - - Adds check for Threads in CMake script - - Issue: #50 - -2014-05-12 Roman Proskuryakov - - Adds cmake module to find LLVM - - Issue: #50 - -2014-05-12 Roman Proskuryakov - - Adds cmake module to find libreadline and libtinfo - - Issue: #50 - -2014-05-12 Roman Proskuryakov - - Moves common vars into cmake/variables.cmake - - Issue: #50 - -2014-05-12 Roman Proskuryakov - - Adds target `make uninstall` - - Issue: #50 - -2014-02-26 Roman Proskuryakov - - Adds CLI arg `-v, --version` - - Issue: #50 - -2014-03-22 Roman Proskuryakov - - Fixes -Wall -Wextra warnings - - Issue: #50 - -2014-01-08 Dmitry Kashitsyn - - Adds include guard to completion engine header - -2013-12-31 Roman Proskuryakov - - Adds handling of READLINE option in cmake - - Now usage of readline library may be controlled - by passing a -DREADLINE=(ON|OFF) parameter to cmake. - - If parameter is passed library usage depends on value. - If parameter is omitted it defaults to ON. - - Issue: #52 - -2013-12-31 Roman Proskuryakov - - Adds a dumb version of CompletionEngine without libreadline - - Issue: #52 - -2013-12-25 Roman Proskuryakov - - Makes the code compile with MinGW - - Issue: #46 - -2013-12-24 Roman Proskuryakov - - Makes Image::loadImage() cross-platform - - It worked fine for GNU/Linux, FreeBSD, but the is no mmap in MS \|/iNdOwS. - The function is rewritten with std::ifstream. - - Issue: #46 - -2014-01-06 Roman Proskuryakov - - Adds a new cmake target to compress and install ChangeLog - - Issue: #43 - -2013-12-31 Roman Proskuryakov - - Removes redundant static_cast for TInteger - - Issue: #51 - -2013-12-31 Roman Proskuryakov - - Implements primitive 'readline' inside CompletionEngine - - Issue: #52 - -2013-12-29 Roman Proskuryakov - - Adds script to generate ChangeLog - - Issue: #43 - -2013-12-29 Roman Proskuryakov - - Adds a template for extended description for deb package - - Issue: #43 - -2013-12-29 Roman Proskuryakov - - Fixes CPACK_SOURCE_IGNORE_FILES - - Target `package_source` should not pack neither .kdev_include_paths nor *.image files. - - Issue: #43 - -2013-12-28 Roman Proskuryakov - - TInteger keeps m_value of type int32_t, not TObject* - - Issue: #51 - -2013-12-28 Roman Proskuryakov - - Fixes JIT compilation - - Issue: #51 - -2013-12-26 Roman Proskuryakov - - Adds TInteger::operator +-(int32_t) - - With these operators we can write a code like: - TInteger x = 42; - int y = x + 2; - - Issue: #51 - -2013-12-22 Roman Proskuryakov - - Replaces `typedef int32_t TInteger` with a real class - - Issue: #51 - -2013-12-07 Roman Proskuryakov - - Fixes compilation warnings for Arch Linux - - Issue: #46 - -2013-12-07 Roman Proskuryakov - - Fixes llvm asserts for Arch Linux - - Issue: #46 - -2013-12-03 Roman Proskuryakov - - Requires CMake 2.8.4 due to cygwin warnings - - Issue: #46 - -2013-12-03 Roman Proskuryakov - - Adds `#include ` to resolve type FILE - - Issue: #46 - -2013-12-03 Roman Proskuryakov - - Adds EOL to args.h - - Issue: #46 - -2013-12-03 Roman Proskuryakov - - Fixes error with undeclared 'EXIT_SUCCESS' for RPi - - Issue: #46 - -2013-12-02 Roman Proskuryakov - - Adds bash completion for llst installed with deb - - Issue: #43 - -2013-12-01 Roman Proskuryakov - - Fixes error: 'unknown type name FILE' - - Issue: #45 - -2013-11-28 Roman Proskuryakov - - Adds the help about llst args - - To display it run `./llst --help`. - - Issue: #33 - -2013-11-27 Roman Proskuryakov - - Adds an empty man for llst - - Issue: #43 - -2013-11-23 Roman Proskuryakov - - Fixes a lot of lintian warnings - - -strips executable - -adds maintainer - -adds dependencies - -changes the name of the project - - Issue: #43 - -2013-11-23 Roman Proskuryakov - - Fix LICENSE due to `http://www.debian.org/doc/debian-policy/ch-docs.html#s-copyrightfile` - - Issue: #43 - -2013-11-23 Roman Proskuryakov - - Adds `make package` - - Issue: #43 - -2013-11-22 Roman Proskuryakov - - Adds `make package_source` - - Issue: #43 - - -[v_0.2.1] -2013-11-18 Dmitry Kashitsyn - - Fixes version in main.cpp - -2013-11-18 Dmitry Kashitsyn - - Fixes readme - - Issue: #23 - - -[v_0.2] -2013-11-18 Dmitry Kashitsyn - - Bumps version to 0.2 - -2013-11-17 Dmitry Kashitsyn - - Adds readme - - Issue: #23 - -2013-11-17 Roman Proskuryakov - - Fixes typos in CompletionEngine - - Issue: #27 - -2013-11-17 Dmitry Kashitsyn - - Moves completion engine init code to the proper place - - Issue: #40 - -2013-11-17 Dmitry Kashitsyn - - Adds copyleft notice to Core.ll and args.h - - Issue: #10 - -2013-11-17 Dmitry Kashitsyn - - Adds copyleft notice to source files - - Issue: #10 - -2013-11-17 Dmitry Kashitsyn - - Fixes s/Lo/Low/ in source files copyleft notice - - Issue: #10 - -2013-11-16 Dmitry Kashitsyn - - Adds specifying comment to TArray::operator[] - - Issue: #27 - -2013-11-16 Dmitry Kashitsyn - - Adds const specifier to isSmallInteger and TObject::cast() - - Issue: #27 - -2013-11-16 Dmitry Kashitsyn - - Links to pthread only in LLVM mode - - llvm-config utility provides required libraries to link to. - We do not need to specify pthread ourselves. - - Issue: #27 - -2013-11-14 Roman Proskuryakov - - Fixes clang warnings about unused arguments of linker - - Issue: #39 - -2013-11-14 Roman Proskuryakov - - Switch to a new radix tree by Yuuki Takano - - Issue: #39 - -2013-11-14 Roman Proskuryakov - - Adds default ctor for TSymbol::TCompareFunctor - - Issue: #39 - -2013-11-13 Roman Proskuryakov - - Renames llvm_types.ll -> Core.ll - - Issue: #27 - -2013-11-13 Roman Proskuryakov - - Calls strerror with strict std:: prefix - - Issue: #27 - -2013-11-13 Roman Proskuryakov - - Fixes correct padding in Image::readObject():byteObject - - Issue: #27 - -2013-11-04 Roman Proskuryakov - - Removes trailing whitespaces - - Issue: #27 - -2013-11-04 Roman Proskuryakov - - Prettifies memory.h - - Removes redundant and outdated comments. - Removes static_cast from volatile experiments. - - Issue: #27 - -2013-11-12 Roman Proskuryakov - - Autodetects 64 bit architecture and sets -m32 - - The parameter is set only on 64-bit OS. - - Now you can compile llst under Raspberry Pi or Ubuntu/i686 - or Ubuntu/x86_64 without any changes in CMakelists.txt. - - Issue: #9 - -2013-11-13 Dmitry Kashitsyn - - Add workaround for JIT compiler to handle soft compiled methods - - In-image Smalltalk compiler produces byte codes with trailing zeroes. - In case of soft VM this is not a problem, because control flow - never reaches them due to default method exit sequence. - - In case of JIT compiler we need to fully process all available - bytecodes because we do not know how control will flow. - - Correct solution will be to fix the in-image Smalltalk compiler. - However it is a complex task that should be handled carefully. - - For now we're just ignoring such zero opcodes treating them as NOP. - - Issue: #26 - -2013-11-11 Dmitry Kashitsyn - - Makes LLVM optional - - In order to build llst with llvm support, pass -DLLVM=ON to cmake - - Issue: #9 - -2013-11-11 Roman Proskuryakov - - Adds templated and casting version of Image::getGlobal<>() - - Issue: #38 - -2013-11-11 Dmitry Kashitsyn - - Adds casting version of TDictionary::find<>() - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Replaces a lot of C-style casts with static_cast - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Fixes specific casts in memory managers - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Fixes casts in pointer arithmetic in memory managers - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Adds static_cast to specialized SmalltalkVM::newObject<>() - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Adds static_cast into hptr_base - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Rewrites unspecialized SmalltalkVM::newObject with static_cast - - Issue: #38 - -2013-10-22 Roman Proskuryakov - - Adds the skeleton of CLI args - - Issue: #33 - -2013-10-16 Roman Proskuryakov - - Adds NonCollect MM - - Issue: #36 - -2013-11-06 Roman Proskuryakov - - Adds templated TArray::getField<>() - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Adds templated TVMExecutionContext::stackPop<>() - - Issue: #38 - -2013-11-06 Roman Proskuryakov - - Adds templated Image::readObject<>() - - Issue: #38 - -2013-11-04 Roman Proskuryakov - - Fixes TArray::operator[] - - The previous implementation used reinterpret_cast which is type-unsafe. - Now the compiler throws error if you try to instantiate TArray and - use operator[]. - - Issue: #34 - -2013-10-19 Roman Proskuryakov - - Prettifies BakerMemoryManager::releaseExternalHeapPointer. - - Issue: #27 - -2013-10-19 Roman Proskuryakov - - Removes unneeded volatile qualifiers. - - Issue: #27 - -2013-10-19 Roman Proskuryakov - - Removes IMemoryManager::registerExternalPointer and IMemoryManager::releaseExternalPointer. - Fixes GenerationalMemoryManager::moveYoungObjects. - - Issue: #27 - -2013-10-31 Roman Proskuryakov - - Fixes Dictionary>>includes: which broke Collection>>printString - - Issue: #35 - -2013-10-31 Roman Proskuryakov - - Fixes Collection>>includes: which broke List>>= - Adds tests for List>>includes: and fixes other tests for class List. - - Issue: #35 - -2013-10-31 Dmitry Kashitsyn - - Reverts List>>select: to original recursive form - - Previous experiments with tail field was not correctly reverted. - One of them was the #select: method. - - Issue: #35 - -2013-10-31 Dmitry Kashitsyn - - Reverts List>>appendList: to it's original state - - Previous experiments with tail field was not correctly reverted. - One of them was the #appendList: method. - - Issue: #35 - -2013-10-30 Roman Proskuryakov - - Adds more tests for class List - - Issue: #35 - -2013-10-18 Roman Proskuryakov - - Fixes the condition for symbols for completion engine. - - Issue: #27 - -2013-10-12 Roman Proskuryakov - - Add strict std:: for standard functions. - - Issue: #27 - -2013-10-12 Dmitry Kashitsyn - - Refactors TArray::operator[] to eliminate unsafe cast - - FreeBSD complains that cast in TArray::operator[] may - break aliasing rules due to access through badly aliased pointers. - - Linux port of GCC on the other hand do not complain. - However we need to eliminate this potential problems. - - This commit slightly modifies the template parameter from T* to T. - Also C-style cast is changed to C++ cast. - - Note: - Current implementation uses reinterpret_cast but technically - static_cast should be OK. Adding static_cast forces compiler - to complain that the cast is invalid. - - TArray is expected to be specialized with descendants - of TObject as T. Therefore static_cast from TObject* to T* should - be considered safe. But it isn't probably due to template nature. - - Needs more investigation though. - - Issue: #34 - -2013-10-12 Dmitry Kashitsyn - - Defines fields and bytes as union in TObject - - Issue: #34 - -2013-10-07 Roman Proskuryakov - - Fixes #include headers. - - Issue: #27 - -2013-10-04 Roman Proskuryakov - - Removes unused parameter 'callingContext' from MethodCompiler::compileMethod. - - Issue: #27 - -2013-10-02 Dmitry Kashitsyn - - Renames MethodCompiler::StackAlloca to TStackObject and adds comment - - Issue: #27 - -2013-10-02 Dmitry Kashitsyn - - Redeclares descendants of TStackValue as struct instead of class - - Issue: #27 - -2013-10-01 Roman Proskuryakov - - Transforms TDictionary::find into a templated function. - - Issue: #30 - -2013-10-01 Roman Proskuryakov - - Add newline at EOF. - - Issue: #27 - -2013-10-01 Roman Proskuryakov - - Removes const_cast from hptr::operator[]. - - Issue: #27 - -2013-10-01 Dmitry Kashitsyn - - Replaces LLVM>>do: to Jit>>do: in doc - - Issue: #27 - -2013-09-30 Roman Proskuryakov - - Uses objectSlot everywhere in the case opcode::assignInstance. - - Issue: #27 - -2013-09-30 Roman Proskuryakov - - Renames TSymbol::less to TSymbol::TCompareFunctor. - - Issue: #20 - -2013-09-30 Dmitry Kashitsyn - - Removes -UNDEBUG due to crash in Jit>>do: - -2013-09-30 Dmitry Kashitsyn - - Removes GC per tick invocation - -2013-09-18 Roman Proskuryakov - - Moves compare operators from TDictionary to TSymbol. - - Issue #20 - -2013-09-21 Roman Proskuryakov - - Reverts List methods. Removes fields tail and head. - - Issue #18 - -2013-09-21 Roman Proskuryakov - - Prettifies the usage of memset function inside LLVM IR. - - Issue #11 - -2013-09-20 Roman Proskuryakov - - MethodCompiler::createArray now uses stack allocation. - - Issue #11 - -2013-09-20 Roman Proskuryakov - - Moves allocateStackObject from JITRuntime to MethodCompiler - - Issue #11 - -2013-09-17 Roman Proskuryakov - - Add tests for class Magnitude - - Issue #19 - -2013-09-17 Roman Proskuryakov - - Fixes comparison operators in class Magnitude - - Issue #19 - -2013-09-19 Roman Proskuryakov - - Removes redundant calls to memset from TObject ctor and TByteObject ctor. - Memory manager will zero it, no need to memset it twice. - We gain ~9% speedup :D - -2013-09-18 Roman Proskuryakov - - Fixes the comparison operator - - Issue #1 - -2013-09-09 Roman Proskuryakov - - Fixes redundant cast and header style in Baker MM - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-llvm-3 for changeset 28f11517e164 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-llvm-2 for changeset 6f7824033410 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-llvm-1 for changeset 4a6bdbdcbae8 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-humbug-5 for changeset b9a122a22fb4 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-humbug-4 for changeset c8878db5b232 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-humbug-3 for changeset 8703e9ed25b4 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-humbug-2 for changeset 520782976b69 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-humbug-1 for changeset b0512aeac249 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-dk-2 for changeset f41ccf4cb6d6 - -2013-09-08 Dmitry Kashitsyn - - Added tag orphan-dk-1 for changeset c47c3ba57ec4 - -2013-09-07 Roman Proskuryakov - - Fixes some cppcheck warnings - -2013-09-07 Roman Proskuryakov - - Fixes common realloc mistake - -2013-09-07 Roman Proskuryakov - - Fixes headers - -2013-09-06 Roman Proskuryakov - - Fixes comparison between signed and unsigned integer expressions - -2013-09-06 Roman Proskuryakov - - Fixes headers - -2013-09-05 Roman Proskuryakov - - Collects GC per each tick - -2013-09-05 Roman Proskuryakov - - Adds more asserts - -2013-09-05 Roman Proskuryakov - - Adds more asserts - -2013-09-05 Roman Proskuryakov - - Fixes volatility in object_ptr - -2013-09-05 Roman Proskuryakov - - Reports about TContext::stack overflow - -2013-09-05 Roman Proskuryakov - - Fix: overflow in TContext::stack - -2013-09-05 Roman Proskuryakov - - Small fixes - -2013-09-05 Roman Proskuryakov - - Adds some cv-qualifiers - -2013-09-05 Roman Proskuryakov - - Removes unused function TSymbol::equalsTo - -2013-09-05 Roman Proskuryakov - - Fixes uninitialized field in Image::ImageWriter - -2013-09-04 Roman Proskuryakov - - Small fixes - -2013-09-03 Roman Proskuryakov - - Fixes some cppcheck warnings - -2013-09-03 Roman Proskuryakov - - Fix magic number in MethodCompiler::doSendBinary - -2013-09-03 Roman Proskuryakov - - Improves LLVM primitive::getClass, primitive::getSize - -2013-09-02 Roman Proskuryakov - - Creates wrap to work with ec.stack - -2013-08-20 Roman Proskuryakov - - Improves llvm_types.ll - -2013-08-20 Roman Proskuryakov - - Improves bool JITRuntime::detectLiteralReceiver - -2013-08-15 Roman Proskuryakov - - Adds more test cases for ListTest>>firstLink, ListTest>>lastLink - -2013-08-15 Roman Proskuryakov - - Add ListTest>>lastLink - -2013-08-15 Roman Proskuryakov - - Fixes ListTest>>firstLink - -2013-08-15 Roman Proskuryakov - - Fixes ListTest>>remove - -2013-08-15 Roman Proskuryakov - - Adds ST test for List>>at:put: - -2013-08-15 Roman Proskuryakov - - Fixes List>>at:put: , List>>at:ifAbsent: - -2013-08-15 Dmitry Kashitsyn - - Fix - -2013-08-15 Dmitry Kashitsyn - - Fixes List - -2013-08-15 Roman Proskuryakov - - Adds ST test for List>>at:put: - -2013-08-15 Roman Proskuryakov - - Adds ST test for List>>firstLink - -2013-08-15 Roman Proskuryakov - - Adds ST test for List>>remove: - -2013-08-15 Roman Proskuryakov - - Add ST test for List>>removeFirst - -2013-08-15 Roman Proskuryakov - - Adds ST test for List>>do: - -2013-08-15 Roman Proskuryakov - - Adds ST test for List>>at: - -2013-08-15 Roman Proskuryakov - - Adds ST test for List - -2013-08-15 Roman Proskuryakov - - Fix comment - -2013-08-15 Dmitry Kashitsyn - - Fixes list - -2013-08-15 Dmitry Kashitsyn - - Refactors List. Adds tail field and takes advantage of it - -2013-08-15 Roman Proskuryakov - - Adds more ST tests - -2013-08-15 Roman Proskuryakov - - Fixes StringTest>>lowerCase. Adds more tests - -2013-08-15 Dmitry Kashitsyn - - Renames LLVM to Jit in image source - -2013-08-11 Roman Proskuryakov - - Fixes JITRuntime::detectLiteralReceiver - -2013-08-11 Roman Proskuryakov - - Fixes the style of the code in JITRuntime::detectLiteralReceiver - -2013-08-11 Roman Proskuryakov - - Fixes the style of the code in Image.cpp - -2013-08-10 Roman Proskuryakov - - Adds JITRuntime::detectLiteralReceiver - -2013-08-10 Dmitry Kashitsyn - - Attempt to perform literal call optimization - -2013-08-10 Roman Proskuryakov - - Fixes llstPass - -2013-08-09 Dmitry Kashitsyn - - Fixes crash in sort test (bug in createDirectBlocks()) - -2013-08-09 Dmitry Kashitsyn - - Debugging crash in patching Collection>>sort: - -2013-08-09 Dmitry Kashitsyn - - Removes llvmBitcodes field from TMethod - -2013-08-09 Dmitry Kashitsyn - - Fixes stack overflow due to direct alloca, removes volatiles - -2013-08-09 Dmitry Kashitsyn - - Cleans up the code - -2013-08-09 Dmitry Kashitsyn - - Fixes stupid bug in root init - -2013-08-07 Dmitry Kashitsyn - - Fixes direct temps creation - -2013-08-07 Dmitry Kashitsyn - - debug temp - -2013-08-07 Dmitry Kashitsyn - - Fixes moveObjects() - -2013-08-07 Dmitry Kashitsyn - - Adds holder cleanup code - -2013-08-07 Dmitry Kashitsyn - - Adds support for stack objects in LLVMMemoryManager - -2013-08-07 Dmitry Kashitsyn - - Fixes direct blocks and disables llst optimization pass - -2013-08-07 Dmitry Kashitsyn - - Rewrites stack object protection using separate pointer - -2013-08-06 Roman Proskuryakov - - Adds more asserts - -2013-08-06 Roman Proskuryakov - - Removes redundant variables - -2013-08-06 Roman Proskuryakov - - Rewrites TObject* TDictionary::find with STL lower_bound - -2013-08-06 Dmitry Kashitsyn - - Second attempt to push through metadata - -2013-08-06 Dmitry Kashitsyn - - Adds allocateStackObject (incorrect) - -2013-08-06 Roman Proskuryakov - - Prettifies class Image (Image::ImageWriter) - -2013-08-06 Dmitry Kashitsyn - - Removes unneeded volatile specifiers - -2013-08-06 Dmitry Kashitsyn - - Fixes direct code - -2013-08-05 Dmitry Kashitsyn - - Setup debug environment - -2013-08-05 Dmitry Kashitsyn - - Passes correct context reference to newContext - -2013-08-05 Dmitry Kashitsyn - - Fixes MethodCompiler::pushBlock() - -2013-08-05 Dmitry Kashitsyn - - Fixes naming - -2013-08-05 Dmitry Kashitsyn - - Fixes patching code - -2013-08-05 Roman Proskuryakov - - Fixes ImageWriter - -2013-08-05 Dmitry Kashitsyn - - Fix & cleanup - -2013-08-05 Dmitry Kashitsyn - - Fixes branching - -2013-08-05 Dmitry Kashitsyn - - Fixes branching and block location - -2013-08-05 Roman Proskuryakov - - Moves writing operations for Image to class ImageWriter: writeWord, writeObject, void writeTo(const char* fileName) - -2013-08-04 Dmitry Kashitsyn - - Fixes direct block init - -2013-08-04 Roman Proskuryakov - - Fixes void Image::writeWord(std::ofstream& os, uint32_t word) - Adds void Image::writeObject(std::ofstream& os, TObject* object) - Adds Image::TImageRecordType Image::getObjectType(TObject* object) - -2013-08-04 Dmitry Kashitsyn - - Fixes crashes - -2013-08-04 Dmitry Kashitsyn - - Fix - -2013-08-04 Dmitry Kashitsyn - - Adds debug tools - -2013-08-04 Dmitry Kashitsyn - - Adds SmallInt check to @getObjectClass() - -2013-08-04 Dmitry Kashitsyn - - Adds recompilation and cache invalidation code - -2013-08-04 Roman Proskuryakov - - Adds Image::writeWord(uint32_t word) - -2013-08-04 Roman Proskuryakov - - Improves Image::readWord() - -2013-08-04 Dmitry Kashitsyn - - Adds receiver & receiverClass loading code - -2013-08-04 Dmitry Kashitsyn - - Adds more context fields init - -2013-08-04 Dmitry Kashitsyn - - Adds direct context initialization code - -2013-08-04 Dmitry Kashitsyn - - Fixes direct method call in createDirectBlocks() - -2013-08-04 Dmitry Kashitsyn - - Adds selector to the call stat output text - -2013-08-04 Dmitry Kashitsyn - - Refactors code to use callSiteIndex instead of callSiteOffset - -2013-08-04 Dmitry Kashitsyn - - Fix - -2013-08-04 Dmitry Kashitsyn - - Fixes fallback call arguments - -2013-08-03 Dmitry Kashitsyn - - Adds switch and phi code to patchCallSite() - -2013-08-03 Dmitry Kashitsyn - - Refactors call patching code - -2013-08-03 Dmitry Kashitsyn - - Adds direct method invocation codegen - -2013-08-02 Dmitry Kashitsyn - - Adds more code to the patch function - -2013-08-02 Dmitry Kashitsyn - - Adds more code to patchCallSite() - -2013-08-02 Dmitry Kashitsyn - - Cleans up indentation in llvm_types.ll - -2013-08-02 Dmitry Kashitsyn - - Adds patchCallSite() stub - -2013-08-01 Dmitry Kashitsyn - - Refactors stats, adds patchHotMethods() code - -2013-08-01 Dmitry Kashitsyn - - Adds patchHotMethods() stub - -2013-07-31 Dmitry Kashitsyn - - Adds site stats for class hits - -2013-07-31 Dmitry Kashitsyn - - Adds call site parameter to jit calls - -2013-07-31 Dmitry Kashitsyn - - temp - -2013-07-31 Dmitry Kashitsyn - - Refactors call site stats code - -2013-07-31 Dmitry Kashitsyn - - Adds method hit stats - -2013-07-31 Dmitry Kashitsyn - - Adds stubs for call site processing in a jit code - -2013-07-18 Dmitry Kashitsyn - - Adds sample thread messaging interface - -2013-07-18 Dmitry Kashitsyn - - Moves assignment to expression instead of statement - -2013-07-17 Dmitry Kashitsyn - - Allows variables in console - -2013-07-17 Dmitry Kashitsyn - - Adds Block>>whileTrue and whileFalse methods - -2013-07-16 Dmitry Kashitsyn - - Adds image builder's header stub - -2013-07-15 Dmitry Kashitsyn - - Fixes grammar - -2013-07-15 Dmitry Kashitsyn - - Fixes reduce/reduce conflict in grammar - -2013-07-15 Dmitry Kashitsyn - - Adds lexer and parser fixes - -2013-07-12 Dmitry Kashitsyn - - Adds more tokens and error recovery rules - -2013-07-12 Dmitry Kashitsyn - - Adds tokens and priority spec - -2013-07-12 Dmitry Kashitsyn - - Fixes grammar - -2013-07-11 Dmitry Kashitsyn - - Adds disassembler code, more grammar - -2013-07-11 Dmitry Kashitsyn - - Adds more grammar - -2013-07-11 Dmitry Kashitsyn - - Adds symbol entry for inline_object - -2013-07-11 Dmitry Kashitsyn - - Adds more grammar - -2013-07-11 Dmitry Kashitsyn - - Picks grammar file from commit b0512ae - -2013-07-11 Dmitry Kashitsyn - - Reverts back to the llvm gc - -2013-07-11 Dmitry Kashitsyn - - Fixes Scheduler>>run - -2013-07-10 Roman Proskuryakov - - Fixes Scheduler. Fixes List>>remove: - -2013-07-08 Dmitry Kashitsyn - - Fixes File>>fileIn and Scheduler debug code - -2013-07-06 Dmitry Kashitsyn - - adds test that segfaults vm - -2013-07-06 Dmitry Kashitsyn - - Fixes Scheduler>>runOnce - -2013-07-06 Dmitry Kashitsyn - - Adds Scheduler and Thread classes - -2013-07-04 Roman Proskuryakov - - Speeds up BakerMemoryManager::releaseExternalHeapPointer - -2013-07-03 Roman Proskuryakov - - Fixes the last merge - -2013-07-02 Dmitry Kashitsyn - - Reverts rootStack due to object_ptr::next looping - -2013-07-02 Dmitry Kashitsyn - - Refactors rootStack to std::list> and ptr fix - -2013-07-02 Dmitry Kashitsyn - - Adds volatile specifier to object_ptr::data - -2013-07-01 Dmitry Kashitsyn - - Fix - -2013-07-01 Dmitry Kashitsyn - - Fixes bug in new hptr<> - -2013-07-01 Dmitry Kashitsyn - - Adds experimental hptr<> implementation using stack objects - -2013-06-07 Roman Proskuryakov - - Adds ST bindins to get the mode of a file: f stat mode asString -> '-rw-r-----' - -2013-06-07 Roman Proskuryakov - - Adds primitive ioFileSetStatIntoArray to get file stat. Adds ST class FileStat - -2013-06-07 Roman Proskuryakov - - Fixes ST class File - -2013-06-05 Roman Proskuryakov - - Changes primitive fopen to open... Modifies ST class File - -2013-06-05 Roman Proskuryakov - - Adds primitives: fopen, fclose, fwrite, fread, fgetc, fputc, fseek - -2013-06-04 Roman Proskuryakov - - Simplifies the code of generational GC - -2013-05-30 Roman Proskuryakov - - Adds more docs - -2013-05-28 Roman Proskuryakov - - Adds info how to run Smalltalk right in LLVM - -2013-05-18 Roman Proskuryakov - - Fixes some memory leaks - -2013-05-18 Roman Proskuryakov - - Backed out changeset: fb3f87d03444 - -2013-05-07 Roman Proskuryakov - - Removes outdated doc - -2013-05-07 Roman Proskuryakov - - Adds more docs - -2013-05-01 Dmitry Kashitsyn - - fix - -2013-05-01 Dmitry Kashitsyn - - GGC fix - -2013-05-01 Dmitry Kashitsyn - - Adds missing inactive pointer setting - -2013-04-27 Dmitry Kashitsyn - - Adds more GGC code - -2013-04-27 Roman Proskuryakov - - Adds traces for TJITContext::popValue - -2013-04-27 Roman Proskuryakov - - Prettifies linkage of blocks in MethodCompiler::writeFunctionBody - -2013-04-27 Roman Proskuryakov - - Fixes EH in soft VM for TContext* and TBlockReturn - -2013-04-26 Roman Proskuryakov - - Renames unaryMessage to unaryBuiltIns, binaryMessage to binaryBuiltIns. - Removes unused special opcode 'breakpoint' - -2013-04-26 Roman Proskuryakov - - Modifies some primitives - -2013-04-26 Dmitry Kashitsyn - - Adds GGC lists research - -2013-04-24 Roman Proskuryakov - - Fixes blockReturn handling in the SoftVM. Adds docs for blockReturn. - -2013-04-23 Roman Proskuryakov - - Adds test for MethodCompiler::TJITContext::popValue - -2013-04-22 Roman Proskuryakov - - Removes SmalltalkVM::rootStack - -2013-04-22 Dmitry Kashitsyn - - Adds GGC benchmark - -2013-04-22 Dmitry Kashitsyn - - Fix - -2013-04-22 Dmitry Kashitsyn - - Changes default image file - -2013-04-22 Dmitry Kashitsyn - - Adds counters and statistics - -2013-04-22 Dmitry Kashitsyn - - Fix - -2013-04-22 Dmitry Kashitsyn - - Code cleanup & fixes - -2013-04-22 Dmitry Kashitsyn - - Fixes comments - -2013-04-22 Roman Proskuryakov - - Small changes - -2013-04-22 Dmitry Kashitsyn - - Adds generational GC code. Not tested yet - -2013-04-21 Dmitry Kashitsyn - - Adds image building rules - -2013-04-19 Roman Proskuryakov - - Adds docs for JIT compilation - -2013-04-19 Dmitry Kashitsyn - - Adds metaclass methods to the completion list - -2013-04-19 Roman Proskuryakov - - Changes the logic of codegeneration of primitives. Adds more comments. - -2013-04-19 Roman Proskuryakov - - Fixes the declaration of compileSmallIntPrimitive - -2013-04-19 Roman Proskuryakov - - Small fix - -2013-04-19 Roman Proskuryakov - - Moves some primitives from vm.cpp to primitives.cpp - -2013-04-19 Roman Proskuryakov - - Adds function callPrimitive - -2013-04-19 Roman Proskuryakov - - Cosmetic changes - -2013-04-18 Roman Proskuryakov - - Fixes dependency graph - -2013-04-19 Dmitry Kashitsyn - - Cleans up the code, stub for meta methods - -2013-04-18 Dmitry Kashitsyn - - Removes trace messages - -2013-04-18 Dmitry Kashitsyn - - Adds smalltalk completion engine for readline library - -2013-04-18 Dmitry Kashitsyn - - Adds trie container and completion backend db - -2013-04-18 Roman Proskuryakov - - Fix - -2013-04-18 Roman Proskuryakov - - Moves the branch of #doesNotUnderstand: to function - -2013-04-18 Roman Proskuryakov - - Moves declaration of exception API to .ll - -2013-04-18 Dmitry Kashitsyn - - Fixes readline - -2013-04-18 Roman Proskuryakov - - Moves declaration of runtime API to .ll - -2013-04-17 Roman Proskuryakov - - Adds how-to call extern member function - -2013-04-17 Roman Proskuryakov - - Backed out changeset 2e2a65a11314 - -2013-04-17 Dmitry Kashitsyn - - Adds readline support. Use 'exit' command to quit - -2013-04-17 Dmitry Kashitsyn - - Comments code - -2013-04-17 Roman Proskuryakov - - Removes dev stuff. Adds exit(1) to unexpected cases. - -2013-04-17 Roman Proskuryakov - - Small changes - -2013-04-16 Roman Proskuryakov - - Moves codegeneration of primitives of SmallInt to MethodCompiler::writeSmallIntPrimitive( ... ) - Renames primitiveSucceeded to primitiveSucceededBB, primitiveFailed to primitiveFailedBB - -2013-04-16 Roman Proskuryakov - - Renames instruction.* to TInstruction.* - -2013-04-16 Roman Proskuryakov - - Removes files with bug reports - -2013-04-16 Roman Proskuryakov - - Backed out changeset d36c6a11061b - -2013-04-16 Roman Proskuryakov - - Removes unnecessary TDeferredValue::isLazy() - -2013-04-16 Roman Proskuryakov - - Removes dev comments - -2013-04-16 Roman Proskuryakov - - Fixes TJITContext::popValue (phi) - -2013-04-07 Dmitry Kashitsyn - - Fix - -2013-04-07 Dmitry Kashitsyn - - Fixes popTop - -2013-04-07 Dmitry Kashitsyn - - Fixes dup - -2013-04-07 Dmitry Kashitsyn - - Fixes dup - -2013-04-07 Dmitry Kashitsyn - - Plays with lazy push - -2013-04-07 Roman Proskuryakov - - Small changes - -2013-04-07 Roman Proskuryakov - - Moves BrokenPointerPass to llstDebuggingPass.cpp - -2013-04-05 Roman Proskuryakov - - "Duplicate" must be fixed - -2013-04-05 Roman Proskuryakov - - Added tag Monkey patching for changeset 03c043a82c0b - -2013-04-05 Roman Proskuryakov - - Fixes PushTemporary in MarkArguments - -2013-04-05 Roman Proskuryakov - - Adds ST test for Push* and MarkArgumens codegeneration - -2013-04-04 Dmitry Kashitsyn - - temp - -2013-04-04 Roman Proskuryakov - - MarkArguments and Push* instructions lead to pointer invalidation - -2013-04-04 Roman Proskuryakov - - Adds pointer check for "self" in sendMessage args - -2013-04-03 Roman Proskuryakov - - Adds pass that checks whether the class of object points to null - -2013-04-02 Roman Proskuryakov - - Renames LLVMTestStatements to StatementsTest. Tests are not forced to run in LLVM - -2013-03-25 Roman Proskuryakov - - Fixes .gdbinit guide - -2013-03-25 Roman Proskuryakov - - Adds a script that builds the image - -2013-03-25 Roman Proskuryakov - - Renames binaryMessage enum - -2013-03-24 Roman Proskuryakov - - Adds TInstruction::toString() - -2013-03-21 Roman Proskuryakov - - Starts init process from soft VM - -2013-03-21 Roman Proskuryakov - - Fixes GC leak - -2013-03-20 Roman Proskuryakov - - Renames globals and base functions - -2013-03-18 Roman Proskuryakov - - Fixes GC leak in primitive::cloneByteObject - -2013-03-16 Roman Proskuryakov - - Adds checkRoot to primitive array:at:put - -2013-03-14 Dmitry Kashitsyn - - Fixes pointer loss in doesNotUnderstand part of sendMessage() - -2013-03-13 Roman Proskuryakov - - ST code results in a segmentation fault - -2013-03-13 Roman Proskuryakov - - Fixes primitive throwError - -2013-03-08 Roman Proskuryakov - - Adds more ST tests - -2013-03-08 Roman Proskuryakov - - Adds more ST tests - -2013-03-07 Roman Proskuryakov - - Starts initial process in LLVM - -2013-03-07 Dmitry Kashitsyn - - Enables llst optimization passes - -2013-03-07 Dmitry Kashitsyn - - Fixes leaking roots, cleans up the code - -2013-03-06 Roman Proskuryakov - - Fixes check of count of args and temps in blockInvoke - -2013-03-06 Roman Proskuryakov - - Removes intrinsic declaration from llvm_types.ll - -2013-03-05 Roman Proskuryakov - - Fixes context->previousContext in LLVM:executeProcess - -2013-03-05 Roman Proskuryakov - - Renames JITRuntime::ot to m_baseTypes - -2013-03-05 Roman Proskuryakov - - Fixes llvm_types.ll indentation - -2013-03-05 Roman Proskuryakov - - Fixes backtrace in method Process>>execute - -2013-03-05 Roman Proskuryakov - - Cleans up LLSTPass - -2013-03-04 Dmitry Kashitsyn - - Fixes blockBytePointer, disables llst opts - -2013-03-03 Roman Proskuryakov - - Adds more ST tests(compiler fails) - -2013-03-03 Roman Proskuryakov - - Adds ST tests - -2013-03-03 Roman Proskuryakov - - Changes EH due to C++ ABI for Itanium docs - -2013-03-02 Roman Proskuryakov - - LLVM: throws TContext*. Fixes JIT->sendMessage messageArguments. - -2013-02-28 Roman Proskuryakov - - Fixes primitive SmallIntDiv - -2013-02-28 Roman Proskuryakov - - Fixes IR generation - -2013-02-27 Roman Proskuryakov - - llstPass: removes unused GC roots - -2013-02-27 Dmitry Kashitsyn - - Fixes Link>>do:, sendMessage and block code optimization - -2013-02-27 Dmitry Kashitsyn - - Code cleanup - -2013-02-27 Roman Proskuryakov - - Small fixes in llstPass - -2013-02-27 Roman Proskuryakov - - Backed out changeset 9e4ba46605c2 - -2013-02-26 Dmitry Kashitsyn - - Adds insertion sort and list size - -2013-02-25 Roman Proskuryakov - - Enqueus createGCLoweringPass() - -2013-02-25 Dmitry Kashitsyn - - Adjusts heap grow criteria - -2013-02-25 Dmitry Kashitsyn - - Fixes growHeap() - -2013-02-25 Roman Proskuryakov - - Fixes indentation - -2013-02-25 Roman Proskuryakov - - Adds llst pass which removes unnecessary loads of GC protected pointers - -2013-02-24 Roman Proskuryakov - - Fixes 9af5a06a331c - -2013-02-24 Dmitry Kashitsyn - - Fixes args on 3.1 branch - -2013-02-24 Dmitry Kashitsyn - - Allows gc to grow heap - -2013-02-24 Dmitry Kashitsyn - - cleanup - -2013-02-24 Dmitry Kashitsyn - - Grow heap stub - -2013-02-23 Dmitry Kashitsyn - - temp - -2013-02-22 Dmitry Kashitsyn - - Fixes LLVM gc - -2013-02-22 Roman Proskuryakov - - Adds .gdbinit support to views - -2013-02-22 Dmitry Kashitsyn - - Removes unused variable - -2013-02-22 Dmitry Kashitsyn - - temp - -2013-02-22 Dmitry Kashitsyn - - Fixes assign instructions - -2013-02-22 Dmitry Kashitsyn - - Cleanup - -2013-02-21 Dmitry Kashitsyn - - Fixes holders - -2013-02-21 Dmitry Kashitsyn - - Removes %struct. prefix - -2013-02-21 Dmitry Kashitsyn - - Renames 'TObject::getField(int)' to 'getObjectField' - -2013-02-21 Dmitry Kashitsyn - - Fixes self holder - -2013-02-21 Dmitry Kashitsyn - - Adds cleanup code, traces - -2013-02-21 Dmitry Kashitsyn - - Removes incorrect protectPointer()-s - -2013-02-21 Dmitry Kashitsyn - - Refactors deferred operations to use getField() - -2013-02-20 Dmitry Kashitsyn - - Fix - -2013-02-20 Dmitry Kashitsyn - - Cleanup - -2013-02-20 Dmitry Kashitsyn - - Refactors preamble and object access (literals, class, etc) - -2013-02-20 Dmitry Kashitsyn - - Probably fixes popValue in the complex cases - -2013-02-20 Dmitry Kashitsyn - - Fixes self access - -2013-02-20 Dmitry Kashitsyn - - Fixes assign opcodes to use deferred values - -2013-02-20 Dmitry Kashitsyn - - Adds more deferred loads, makes code compile - -2013-02-20 Roman Proskuryakov - - Adds pretty prints for gdb - -2013-02-19 Dmitry Kashitsyn - - Adds more code for deferred value support - -2013-02-19 Roman Proskuryakov - - Fixes tclass view(instance size) - -2013-02-19 Roman Proskuryakov - - Adds llst-views.gdb (TSymbol, TMethod, TClass) - -2013-02-19 Roman Proskuryakov - - Small fixes for primitive::bulkReplace - -2013-02-19 Roman Proskuryakov - - Moves functions from llvm_types.ll to struct TBaseFunctions. Adds trailing spaces to MethodCompiler.cpp - -2013-02-19 Roman Proskuryakov - - Renames ot to m_baseTypes - -2013-02-19 Dmitry Kashitsyn - - temp - -2013-02-18 Roman Proskuryakov - - Adds trailing spaces. Removes magic numbers(sendBinary) - -2013-02-18 Dmitry Kashitsyn - - Adds deferred functional to value stack (stub) - -2013-02-18 Roman Proskuryakov - - Adds forgotten file(fixes 5e59eadd1414) - -2013-02-18 Roman Proskuryakov - - Moves enums from SmalltalkVM class to opcodes.h - -2013-02-18 Dmitry Kashitsyn - - gc root research - -2013-02-17 Roman Proskuryakov - - Fixes forming of blockFunctionName - -2013-02-16 Dmitry Kashitsyn - - Fixes GC interface - -2013-02-16 Dmitry Kashitsyn - - Fixes compile error - -2013-02-16 Dmitry Kashitsyn - - Removes traces, sets -O0 - -2013-02-16 Dmitry Kashitsyn - - Working 3.1 branch with some forward changes - -2013-02-10 Roman Proskuryakov - - Adds comments to throwError primitive - -2013-02-10 Roman Proskuryakov - - Fixes boundary checks in string|array At:Put: primitives - -2013-02-10 Roman Proskuryakov - - Adds SmallIntShift primitive - -2013-02-10 Roman Proskuryakov - - Adds throwError primitive. Fixes startNewProcess primitive. - -2013-02-10 Dmitry Kashitsyn - - Fixes allocateRoot() - -2013-02-09 Dmitry Kashitsyn - - Adds GC root stuff - -2013-02-08 Dmitry Kashitsyn - - Fixes caches - -2013-02-08 Dmitry Kashitsyn - - Adds runtime stats - -2013-02-08 Roman Proskuryakov - - Adds #doesNotUnderstand: handler - -2013-02-08 Roman Proskuryakov - - Adds sendToSuper special opcode - -2013-02-07 Roman Proskuryakov - - Fixes receiverClass in sendMessage - -2013-02-07 Roman Proskuryakov - - Fixes sendMessageArgs - -2013-02-06 Dmitry Kashitsyn - - Adds receiverClass parameter - -2013-02-06 Dmitry Kashitsyn - - Fixes cloneObject primitive - -2013-02-06 Roman Proskuryakov - - Adds isKindOf test - -2013-02-06 Roman Proskuryakov - - Adds ST test which localises Class>>asString problem - -2013-02-06 Roman Proskuryakov - - Adds more tests. Class asString fails. - -2013-02-06 Roman Proskuryakov - - Adds TODO for startNewProcess stub - -2013-02-06 Roman Proskuryakov - - Adds startNewProcess stub - -2013-02-05 Roman Proskuryakov - - Adds module pass manager - -2013-02-05 Dmitry Kashitsyn - - Enables optimizations - -2013-02-05 Dmitry Kashitsyn - - temp - -2013-02-05 Dmitry Kashitsyn - - Simplifies caches - -2013-02-05 Dmitry Kashitsyn - - Fixes cache - -2013-02-05 Dmitry Kashitsyn - - Adds function and block caches to runtime, comments out traces - -2013-02-05 Dmitry Kashitsyn - - Adds a few more tests - -2013-02-04 Dmitry Kashitsyn - - Fixes primitives, adds meta testing code - -2013-02-04 Dmitry Kashitsyn - - Fixes bulkReplace - -2013-02-04 Dmitry Kashitsyn - - Fixes bulkReplace primitive (function call still fails though) - -2013-02-04 Dmitry Kashitsyn - - Adds strcat test - -2013-02-04 Dmitry Kashitsyn - - Fix - -2013-02-04 Dmitry Kashitsyn - - Fixes popValue() and adds missing pop chains - -2013-02-04 Dmitry Kashitsyn - - Fixes at:[put:] vs jit.popValue - -2013-02-04 Dmitry Kashitsyn - - Fixes blockInvoke and value stack refactoring - -2013-02-03 Dmitry Kashitsyn - - Adds some FIXME notes - -2013-02-03 Dmitry Kashitsyn - - Fixes hasValue() - -2013-02-03 Dmitry Kashitsyn - - Fixes popValue - -2013-02-03 Dmitry Kashitsyn - - Reworks the value stack - -2013-02-02 Dmitry Kashitsyn - - temp - -2013-02-01 Dmitry Kashitsyn - - temp - -2013-02-01 Dmitry Kashitsyn - - Fixes block invocation (LOL). Note: testblockParam fails - -2013-01-31 Dmitry Kashitsyn - - Fixes test - -2013-01-31 Dmitry Kashitsyn - - Adds blockParam test - -2013-01-31 Roman Proskuryakov - - Fixes rethrow in landingpad BB - -2013-01-30 Roman Proskuryakov - - Adds ST closure test - -2013-01-30 Roman Proskuryakov - - Fixes allocate* primitives - -2013-01-30 Roman Proskuryakov - - Fixes smallint primitive - -2013-01-30 Dmitry Kashitsyn - - Fixes getClass primitive - -2013-01-30 Dmitry Kashitsyn - - Fixes scanners code - -2013-01-30 Dmitry Kashitsyn - - Fixes scanForBranches - -2013-01-30 Dmitry Kashitsyn - - Test code disassembly - -2013-01-29 Roman Proskuryakov - - Adds some ST tests. - -2013-01-27 Dmitry Kashitsyn - - Fixes testBlockReturn - -2013-01-26 Roman Proskuryakov - - Adds primitive getClass of SmallInt - -2013-01-26 Roman Proskuryakov - - Error in scanForBranches - -2013-01-26 Roman Proskuryakov - - Adds putChar primitive. Fixes indentation - -2013-01-27 Dmitry Kashitsyn - - Fixes array at:put: - -2013-01-26 Dmitry Kashitsyn - - Adds checking code to array at:[put:] - -2013-01-26 Dmitry Kashitsyn - - Fixes pushInstance - -2013-01-26 Dmitry Kashitsyn - - Fixes typos, adds comments - -2013-01-26 Dmitry Kashitsyn - - Sets -O0 in cmake lists - -2013-01-26 Roman Proskuryakov - - Fixes getSize primitive - -2013-01-26 Roman Proskuryakov - - Fixes primitive logic - -2013-01-26 humbug - - Fixes invoke and smallint primitives - -2013-01-26 Roman Proskuryakov - - Fixes stringAt:[Put:] primitive - -2013-01-25 Dmitry Kashitsyn - - Fixes pushInstance - -2013-01-25 Dmitry Kashitsyn - - Adds at: and at:put: primitives - -2013-01-25 Dmitry Kashitsyn - - Fixes block invoke - -2013-01-24 Roman Proskuryakov - - Fixes indentation - -2013-01-24 Roman Proskuryakov - - Fixes merge - -2013-01-25 Dmitry Kashitsyn - - Fixes blockInvoke - -2013-01-24 Dmitry Kashitsyn - - Optimizes self - -2013-01-24 Roman Proskuryakov - - Implements smallint primitives with llvm API - -2013-01-24 Dmitry Kashitsyn - - Fixes doPrimitive - -2013-01-24 Dmitry Kashitsyn - - Fixes doPrimitive - -2013-01-24 Roman Proskuryakov - - Fixes primitive functions' mapping - -2013-01-24 Dmitry Kashitsyn - - Comments out some traces - -2013-01-24 Dmitry Kashitsyn - - Fixes sendMessage - -2013-01-24 Dmitry Kashitsyn - - Fixes createArray - -2013-01-24 Dmitry Kashitsyn - - Fixes runtime init code - -2013-01-23 Roman Proskuryakov - - Fixes EE initialization - -2013-01-23 Roman Proskuryakov - - Fixes rethrow BB - -2013-01-23 Dmitry Kashitsyn - - Minor fixes - -2013-01-22 Dmitry Kashitsyn - - Fixes trailing spaces - -2013-01-22 Dmitry Kashitsyn - - Fixes indentation - -2013-01-22 Dmitry Kashitsyn - - Adds blockInvoke - -2013-01-21 Dmitry Kashitsyn - - Adds small int primitives - -2013-01-21 Dmitry Kashitsyn - - Adds bulkReplace primitive - -2013-01-21 Dmitry Kashitsyn - - Fixes getSlotSize() - -2013-01-21 Dmitry Kashitsyn - - Adds primitive code; removes trailing spaces - -2013-01-20 Dmitry Kashitsyn - - Minor fixes - -2013-01-20 Dmitry Kashitsyn - - Fixes blockReturn - -2013-01-20 Dmitry Kashitsyn - - Fixes branch instructions - -2013-01-20 Dmitry Kashitsyn - - Fixes branching - -2013-01-20 Dmitry Kashitsyn - - Fixes scanForBlockReturn - -2013-01-20 Roman Proskuryakov - - Fixes landingpad clause. Fixes CFG - -2013-01-20 Roman Proskuryakov - - Fixes CFG - -2013-01-20 Dmitry Kashitsyn - - Fixes logic errors - -2013-01-20 Dmitry Kashitsyn - - Fixes landing pad - -2013-01-20 Dmitry Kashitsyn - - Adds scanForBlockReturn, fixes logic. Not debugged yet - -2013-01-19 Dmitry Kashitsyn - - Refactors typeInfo acquiring code - -2013-01-17 Dmitry Kashitsyn - - Fixes assignInstance - -2013-01-17 Dmitry Kashitsyn - - Adds checkRoot() API interface; refactors TJITContext - -2013-01-17 Dmitry Kashitsyn - - Fixes sendMessage; some cleanup - -2013-01-17 Dmitry Kashitsyn - - Renames landing pad - -2013-01-17 Dmitry Kashitsyn - - Cleans up the code - -2013-01-17 Dmitry Kashitsyn - - Small fixes - -2013-01-16 Dmitry Kashitsyn - - Adds exception aware code to the sendMessage - -2013-01-16 Dmitry Kashitsyn - - Adds exception aware code to the sendBinary - -2013-01-16 Dmitry Kashitsyn - - Adds landing pad code - -2013-01-16 Dmitry Kashitsyn - - Fixes stupid code - -2013-01-16 Dmitry Kashitsyn - - Inlines smallInt literals directly as consts - -2013-01-16 Dmitry Kashitsyn - - Fixes naming - -2013-01-16 Dmitry Kashitsyn - - Adds some names - -2013-01-16 Roman Proskuryakov - - Adds writeLandingpadBB stub - -2013-01-16 Roman Proskuryakov - - Fixes ST loop test - -2013-01-15 Dmitry Kashitsyn - - temp - -2013-01-15 Dmitry Kashitsyn - - Fixes globals mapping - -2013-01-15 Dmitry Kashitsyn - - Fixes sendBinary - -2013-01-15 Dmitry Kashitsyn - - Adds block test - -2013-01-14 Roman Proskuryakov - - Adds JITRuntime::initializeRuntimeAPI() - -2013-01-14 Roman Proskuryakov - - Moves m_functionPassManager init to method - -2013-01-14 Roman Proskuryakov - - Small cleanup - -2013-01-14 Roman Proskuryakov - - Removes m_compiledFunctions - -2013-01-14 Dmitry Kashitsyn - - Fixes pushBlock - -2013-01-14 Dmitry Kashitsyn - - Adds blockReturn code - -2013-01-14 Dmitry Kashitsyn - - Interfaces createBlock() and emitBlockReturn() to the JIT - -2013-01-14 Dmitry Kashitsyn - - Adds emitBlockReturn() - -2013-01-14 Dmitry Kashitsyn - - Adds createBlock - -2013-01-14 Roman Proskuryakov - - Adds llvm stub: C++ exception handling - -2013-01-14 Dmitry Kashitsyn - - Adds function pass manager code; some more simple tests - -2013-01-13 Dmitry Kashitsyn - - Fixes runtime - -2013-01-13 Dmitry Kashitsyn - - Fixes function preamble - -2013-01-13 Dmitry Kashitsyn - - Adds testing code - -2013-01-13 Dmitry Kashitsyn - - Fixes pointers - -2013-01-13 Dmitry Kashitsyn - - Fixes runtime - -2013-01-13 Dmitry Kashitsyn - - Adds runtime test - -2013-01-12 Dmitry Kashitsyn - - Adds scanForBranches pass for blocks - -2013-01-12 Dmitry Kashitsyn - - Fixes scanForBranches - -2013-01-12 Dmitry Kashitsyn - - Fixes compiler. Now compiles valid module - -2013-01-12 Dmitry Kashitsyn - - Fixes branching - -2013-01-12 Dmitry Kashitsyn - - Fixes compiler types - -2013-01-12 Dmitry Kashitsyn - - Fixes message arguments and function prototypes - -2013-01-12 Dmitry Kashitsyn - - Fixes compiler - -2013-01-12 Dmitry Kashitsyn - - Fixes compiler. Adds test method LLVMTest>>test1. See init.txt - -2013-01-11 Dmitry Kashitsyn - - Fixes compiler - -2013-01-11 Dmitry Kashitsyn - - Adds more twines - -2013-01-11 Dmitry Kashitsyn - - Fixes naming and args array manipulation - -2013-01-11 Dmitry Kashitsyn - - Fixes sendUnary, sendMessage and other stuff. Now compiles - -2013-01-11 Roman Proskuryakov - - Monkey patching: block preamble - -2013-01-11 Dmitry Kashitsyn - - Fixes block creation code - -2013-01-11 Dmitry Kashitsyn - - Adds more compiler refactoring - -2013-01-10 Dmitry Kashitsyn - - Fixes markArguments - -2013-01-10 Dmitry Kashitsyn - - Refactors runtime API methods - -2013-01-10 Dmitry Kashitsyn - - Refactors doSpecial - -2013-01-10 Dmitry Kashitsyn - - Refactors names - -2013-01-10 Dmitry Kashitsyn - - Moves IRBuilder<> to TJITContext - -2013-01-10 Dmitry Kashitsyn - - Moves TInstruction to TJITContext - -2013-01-10 Dmitry Kashitsyn - - Refactors MethodCompiler - -2013-01-10 Roman Proskuryakov - - Fixes preamble - -2013-01-10 Roman Proskuryakov - - Fixes branchIf* - -2013-01-10 Roman Proskuryakov - - Prettifies globals - -2013-01-09 Dmitry Kashitsyn - - Cleanup - -2013-01-09 Dmitry Kashitsyn - - Fixes pushConstant - -2013-01-09 Dmitry Kashitsyn - - Fix - -2013-01-09 Dmitry Kashitsyn - - Fixes conditional branches - -2013-01-09 Dmitry Kashitsyn - - Cleans up the code - -2013-01-09 Dmitry Kashitsyn - - Adds sendMessage bindings to bytecode compiler - -2013-01-09 Dmitry Kashitsyn - - Adds more sendMessage code - -2013-01-09 Dmitry Kashitsyn - - Adds sendMessage stubs - -2013-01-09 Dmitry Kashitsyn - - Code cleanup - -2013-01-09 Roman Proskuryakov - - Fixes writePreamble - -2013-01-09 Roman Proskuryakov - - Removes build.sh - -2013-01-09 Dmitry Kashitsyn - - Small fixes in the image sources - -2013-01-08 Dmitry Kashitsyn - - Reverts image source - -2013-01-08 Dmitry Kashitsyn - - Cleans up the code - -2013-01-08 Roman Proskuryakov - - Updates sendUnary opcode - -2013-01-08 Roman Proskuryakov - - Fixes typo - -2013-01-08 Roman Proskuryakov - - Fixes cmake script - -2013-01-08 Roman Proskuryakov - - Adds error reporting - -2013-01-08 Roman Proskuryakov - - Moves TGlobals structure to llvm_types.ll - -2013-01-07 Dmitry Kashitsyn - - Adds global objects mapping to the JIT module - -2013-01-07 Dmitry Kashitsyn - - humbug -> llvm - -2013-01-07 Roman Proskuryakov - - Adds llvm stub: globals - -2013-01-07 Dmitry Kashitsyn - - Fixes JITRuntime initialization - -2013-01-07 Dmitry Kashitsyn - - Adds wrapper type stubs - -2013-01-07 Dmitry Kashitsyn - - Adds wrappers for newObject functions - -2013-01-07 Dmitry Kashitsyn - - Fixes cmake script. Now compiles correctly - -2013-01-07 Roman Proskuryakov - - Adds module verifier. Small fixes - -2013-01-07 Roman Proskuryakov - - Moves test_llvm_types.cpp to llvm_stub - -2013-01-07 Roman Proskuryakov - - Adds missing Interpreter.h - -2013-01-07 Dmitry Kashitsyn - - Adds JIT init code and simple test - -2013-01-07 Dmitry Kashitsyn - - Adds sendUnary stub - -2013-01-07 Dmitry Kashitsyn - - Adds conditional branching code - -2013-01-07 Dmitry Kashitsyn - - Adds branching code for MethodCompiler, dumb build script - -2013-01-06 Roman Proskuryakov - - Adds llvm stub: changing function on the fly - -2013-01-06 Dmitry Kashitsyn - - Adds some thoughts on branching - -2013-01-06 Dmitry Kashitsyn - - Updates imageSource.st - -2013-01-05 Dmitry Kashitsyn - - Code refactoring and cleanup - -2013-01-05 Dmitry Kashitsyn - - Adds LLVM bindings to cmake script (not working yet) - -2013-01-05 Dmitry Kashitsyn - - Minor fixes - -2013-01-04 Roman Proskuryakov - - Adds IR getField functions - -2013-01-04 Roman Proskuryakov - - Fixes Context.arguments GEP. Fixes selfReturn - -2013-01-05 Dmitry Kashitsyn - - Small typo fixes - -2013-01-04 Roman Proskuryakov - - Small fixes - -2013-01-04 Roman Proskuryakov - - Adds IR getIntegerValue() function - -2013-01-04 Roman Proskuryakov - - Fixes sendBinary stub - -2013-01-04 Dmitry Kashitsyn - - Adds sendBinary instruction stub - -2013-01-04 Dmitry Kashitsyn - - Fix - -2013-01-04 Dmitry Kashitsyn - - Fixes assign instructions - -2013-01-04 Dmitry Kashitsyn - - Adds markArguments instruction - -2013-01-04 Dmitry Kashitsyn - - Adds more instructions - -2013-01-03 Dmitry Kashitsyn - - Refactors MethodCompiler, adds comments - -2013-01-03 Dmitry Kashitsyn - - Refactors compiler - -2013-01-03 Roman Proskuryakov - - Adds more stub for method compiler - -2013-01-03 Dmitry Kashitsyn - - Adds stub for the method compiler - -2013-01-03 Dmitry Kashitsyn - - Adds jit.h - -2013-01-02 Dmitry Kashitsyn - - Adds LLVM Memory Manager - -2013-01-01 Roman Proskuryakov - - Adds comments to llvm types - -2013-01-01 Roman Proskuryakov - - Adds llvm types - -2012-12-31 Dmitry Kashitsyn - - Cleans up code - -2012-12-30 Dmitry Kashitsyn - - Refactors Collection>>sort - -2012-12-29 Dmitry Kashitsyn - - Fixes Collection>>sort - -2012-12-29 Dmitry Kashitsyn - - Fixes Collection>>sort - -2012-12-28 Dmitry Kashitsyn - - Fixes hptr<> - -2012-12-28 Dmitry Kashitsyn - - Refactors hptr<> - -2012-12-28 Dmitry Kashitsyn - - Refactors VM code (minor fixes) - -2012-12-28 Dmitry Kashitsyn - - Cleans up the code - -2012-12-28 Dmitry Kashitsyn - - Added tag v 0.1 for changeset 0c8089e4a8ed - -2012-12-23 Roman Proskuryakov - - Refactors onCollectionOccured as Observer pattern. Cleanups code - -[v_0.1] -2012-12-28 Dmitry Kashitsyn - - Style fixes - -2012-12-28 Dmitry Kashitsyn - - Adds licensing information - -2012-12-28 Dmitry Kashitsyn - - Cleans up the code - -2012-12-28 Roman Proskuryakov - - Removes unnecessary stuff, changes the declaration of execute() method. - -2012-12-28 Roman Proskuryakov - - Small cosmetic fixes - -2012-12-28 Roman Proskuryakov - - Removes test from main - -2012-12-28 Dmitry Kashitsyn - - Fixes the primitive code - -2012-12-27 Roman Proskuryakov - - Cleans up the code - -2012-12-28 Dmitry Kashitsyn - - Adds imageBuilder from lst5 and updates testImage - -2012-12-27 Dmitry Kashitsyn - - Adds some benchmarks, cleans up the code - -2012-12-26 Roman Proskuryakov - - ST -> IR code stub - -2012-12-26 Dmitry Kashitsyn - - Adds Collection>>sort: and sort benchmark - -2012-12-26 Dmitry Kashitsyn - - Adds timer routines - -2012-12-26 Dmitry Kashitsyn - - Cleans up code, adds more stats - -2012-12-26 Dmitry Kashitsyn - - Fixes bulkReplace; minor changes in the test image - -2012-12-24 Roman Proskuryakov - - Modifies LoopTest. Adds new info about Array>>allMethods failure - -2012-12-24 Roman Proskuryakov - - Adds ST PrimitiveTest - -2012-12-23 Roman Proskuryakov - - Adds ST ContextTest - -2012-12-23 Roman Proskuryakov - - Backout 0e3c1e476091 - -2012-12-23 Roman Proskuryakov - - Small fixes - -2012-12-23 Roman Proskuryakov - - Removes process rootStack due to hptr. - -2012-12-22 Roman Proskuryakov - - Changes format of method #error:. - -2012-12-23 Dmitry Kashitsyn - - Temp - -2012-12-22 Roman Proskuryakov - - Adds some ST tests - -2012-12-22 Dmitry Kashitsyn - - Removes unneeded hptrs - -2012-12-22 Dmitry Kashitsyn - - Fixes invalid stack issue in sendMessage vs GC - -2012-12-21 Dmitry Kashitsyn - - Adds more info in #badIndex - -2012-12-21 Dmitry Kashitsyn - - Adds tail call optimization in the sendMessage - -2012-12-20 Dmitry Kashitsyn - - Minor fixes - -2012-12-19 Dmitry Kashitsyn - - Fixes doSendMessage() so it correctly handles the #doesNotUnderstand: case - -2012-12-19 Dmitry Kashitsyn - - Fixes blockInvoke - -2012-12-19 Dmitry Kashitsyn - - Small fixes - -2012-12-19 Roman Proskuryakov - - Fixes segfault - -2012-12-19 Dmitry Kashitsyn - - Refactors GC code - -2012-12-19 Dmitry Kashitsyn - - Cleans up the code - -2012-12-19 Dmitry Kashitsyn - - Fixes GC - -2012-12-19 Dmitry Kashitsyn - - Small fixes for VM vs Process - -2012-12-19 Dmitry Kashitsyn - - Fixes test - -2012-12-18 Dmitry Kashitsyn - - Refactors moveObject - -2012-12-18 Dmitry Kashitsyn - - temp - -2012-12-18 Dmitry Kashitsyn - - Adds new moveObject stub - -2012-12-16 Dmitry Kashitsyn - - Addss some tests - -2012-12-16 Roman Proskuryakov - - Adds test of GC fail - -2012-12-14 Dmitry Kashitsyn - - Fixes imageSource - -2012-12-14 Roman Proskuryakov - - Fixes string test - -2012-12-14 Dmitry Kashitsyn - - Small fixes in VM - -2012-12-14 Roman Proskuryakov - - Adds Array test stub. Fixes context args - -2012-12-14 Roman Proskuryakov - - Cosmetic fixes - -2012-12-14 Dmitry Kashitsyn - - Adds small fix to bulkReplace - -2012-12-14 Dmitry Kashitsyn - - Adds newObject() specializations; small fixes - -2012-12-13 Dmitry Kashitsyn - - Fixes method cache, adds VM statistics - -2012-12-13 Dmitry Kashitsyn - - Fixes bulkReplace && TInteger - -2012-12-13 Dmitry Kashitsyn - - Fixes typos - -2012-12-12 Dmitry Kashitsyn - - Adds comment to the hptr<> - -2012-12-12 Dmitry Kashitsyn - - Adds classdoc comments in types.h - -2012-12-12 Roman Proskuryakov - - Fixes push/pop process - -2012-12-12 Roman Proskuryakov - - Fixes string concat - -2012-12-12 Roman Proskuryakov - - Adds comments - -2012-12-12 Roman Proskuryakov - - Fixes error primitive crash - -2012-12-12 Roman Proskuryakov - - Fixes tests typo - -2012-12-12 Roman Proskuryakov - - Fixes process stack. Now exits normally. - -2012-12-11 Dmitry Kashitsyn - - Fixes loopup error message, comments out GC traces - -2012-12-11 Roman Proskuryakov - - Small image fixes: Test classes, runAll. - -2012-12-11 Dmitry Kashitsyn - - Fixes GC (at last) - -2012-12-11 Dmitry Kashitsyn - - Adds trace messages to GC - -2012-12-11 Dmitry Kashitsyn - - Fixes bulkReplace - -2012-12-11 Dmitry Kashitsyn - - Adds error reporting; code cleanup - -2012-12-11 Dmitry Kashitsyn - - Adds missing *failed = true - -2012-12-11 Dmitry Kashitsyn - - Fixes call to newBinaryObject() - -2012-12-11 Dmitry Kashitsyn - - Minor fixes - -2012-12-11 Dmitry Kashitsyn - - Fixes process - -2012-12-11 Dmitry Kashitsyn - - Fixes crash - -2012-12-10 Dmitry Kashitsyn - - Fixes backTrace - -2012-12-10 Dmitry Kashitsyn - - Cosmetic fixes - -2012-12-10 Dmitry Kashitsyn - - Fix - -2012-12-10 Dmitry Kashitsyn - - Fixes at:put: and bulkReplace: - -2012-12-10 Dmitry Kashitsyn - - Fixes static heap initialization - -2012-12-10 Dmitry Kashitsyn - - Fix - -2012-12-10 Dmitry Kashitsyn - - Fixes static roots - -2012-12-10 Roman Proskuryakov - - Adds ST tests: SmallInt, Compare, Branch, Loop, String, Method lookup. 4171 objects - -2012-12-09 Roman Proskuryakov - - Adds failPrimitive stub. Primitive fails on opcode error(19) - -2012-12-09 Roman Proskuryakov - - Adds image source. Removes gui init from main. The image contains 3891 objects - -2012-12-09 Dmitry Kashitsyn - - Parses argv[1] as image filename - -2012-12-09 Dmitry Kashitsyn - - Adds GC info messages - -2012-12-09 Dmitry Kashitsyn - - Fixes hptr<> - -2012-12-09 Dmitry Kashitsyn - - Fixes GC - -2012-12-08 Dmitry Kashitsyn - - Fixes pointer safety - -2012-12-08 Dmitry Kashitsyn - - Fixes new(Ord|B)inaryObject - -2012-12-07 Dmitry Kashitsyn - - Removes redundant hptr<> - -2012-12-07 Dmitry Kashitsyn - - Removes unused variables - -2012-12-07 Dmitry Kashitsyn - - Fixes sendToSuper - -2012-12-07 Dmitry Kashitsyn - - Refactors rootStack to be part of TVMExecutionContext - -2012-12-07 Dmitry Kashitsyn - - Cleans up and comments the code - -2012-12-07 Dmitry Kashitsyn - - Adds isInStaticHeap() body - -2012-12-07 Dmitry Kashitsyn - - Fixes newPointer() - -2012-12-07 Dmitry Kashitsyn - - Fixes hptr registration - -2012-12-07 Dmitry Kashitsyn - - Fixes hptr<> constructor - -2012-12-07 Dmitry Kashitsyn - - Fixes hptr<> - -2012-12-07 Dmitry Kashitsyn - - Adds bulkReplace logic - -2012-12-07 Dmitry Kashitsyn - - Code cleanup - -2012-12-07 Dmitry Kashitsyn - - Fixes crash - -2012-12-07 Dmitry Kashitsyn - - Adds primitive numbers to code comments - -2012-12-07 Dmitry Kashitsyn - - Fixes runtime bugs - -2012-12-06 Roman Proskuryakov - - Fixes bulkReplace logic. - -2012-12-05 Roman Proskuryakov - - Inserts bulkReplace into doPrimitive - -2012-12-05 Dmitry Kashitsyn - - Adds more memory management code - -2012-12-05 Dmitry Kashitsyn - - Refactors VM code by applying hptr<> to critical pointers - -2012-12-05 Dmitry Kashitsyn - - Adds some more thoughts on hptr<> - -2012-12-05 Roman Proskuryakov - - Bulk replace todos - -2012-12-05 Roman Proskuryakov - - BulkReplace fix - -2012-12-05 Dmitry Kashitsyn - - Fixes TArray - -2012-12-05 Roman Proskuryakov - - Adds bulkReplace stub - -2012-12-04 Dmitry Kashitsyn - - Fixes hptr<>::operator[] - -2012-12-04 Dmitry Kashitsyn - - Fix - -2012-12-04 Dmitry Kashitsyn - - Adds some research on hptr<> - -2012-12-04 Dmitry Kashitsyn - - Adds extrenal pointers support code - -2012-12-04 Dmitry Kashitsyn - - Adds newObject() - -2012-12-04 Roman Proskuryakov - - Implements sendBinary. Adds default primitive stub - -2012-12-04 Dmitry Kashitsyn - - Temp - -2012-12-03 Roman Proskuryakov - - Refactors code. Fixes blockReturn - -2012-12-03 Dmitry Kashitsyn - - Adds GC status flags - -2012-12-03 Dmitry Kashitsyn - - Fixes backtrace - -2012-12-03 Dmitry Kashitsyn - - Refactors sendUnary to a function - -2012-12-03 Dmitry Kashitsyn - - Adds TVMExecutionContext and refactors execution routines - -2012-12-03 Roman Proskuryakov - - GC fails. FIXME - -2012-12-03 Dmitry Kashitsyn - - Adds backtrace printing method - -2012-12-02 Dmitry Kashitsyn - - Fixes block invoke (reverts previousContext) - -2012-12-02 Dmitry Kashitsyn - - Fixes merge - -2012-12-02 Dmitry Kashitsyn - - Fix - -2012-12-02 Dmitry Kashitsyn - - Fixes block invokation, adds stub for in:at:put: - -2012-12-02 Roman Proskuryakov - - Fixes stackReturn and selfReturn. Other little fixes - -2012-12-02 Dmitry Kashitsyn - - Adds Number>>to:do: disasm - -2012-12-02 Dmitry Kashitsyn - - More disasm comments - -2012-12-02 Dmitry Kashitsyn - - More comments - -2012-12-02 Dmitry Kashitsyn - - More comments - -2012-12-02 Dmitry Kashitsyn - - Adds init sequence disassembly - -2012-12-01 Dmitry Kashitsyn - - Fix - -2012-12-01 Dmitry Kashitsyn - - Fixes block invoke - -2012-12-01 Dmitry Kashitsyn - - Adds error reporting in sendMessage - -2012-12-01 Dmitry Kashitsyn - - Fixes block invocation flow - -2012-12-01 Dmitry Kashitsyn - - Refactors doExecutePrimitive - -2012-12-01 Dmitry Kashitsyn - - Fixes runtime - -2012-12-01 Dmitry Kashitsyn - - Refactors pushBlock. Adds comments, throws stupid code away - -2012-12-01 Dmitry Kashitsyn - - Renames context to currentContext in execute - -2012-12-01 Dmitry Kashitsyn - - Fix - -2012-12-01 Dmitry Kashitsyn - - Adds pushBlock opcode - -2012-12-01 Roman Proskuryakov - - doPrimitive opcode fix. Adds comments - -2012-11-30 Roman Proskuryakov - - Fix - -2012-12-01 Dmitry Kashitsyn - - Adds error reporting - -2012-11-30 Dmitry Kashitsyn - - Fixes sendMessage - -2012-11-30 Dmitry Kashitsyn - - Fixes readObject() ints - -2012-11-30 Dmitry Kashitsyn - - Fix - -2012-11-30 Dmitry Kashitsyn - - Fixes sendMessage - -2012-11-30 Dmitry Kashitsyn - - Runtime fixes - -2012-11-30 Dmitry Kashitsyn - - Fixes object size vs slot size issue - -2012-11-30 Roman Proskuryakov - - FIXME - -2012-11-30 Roman Proskuryakov - - Adds comments - -2012-11-30 Roman Proskuryakov - - Adds sendMessage opcode stub - -2012-11-30 Roman Proskuryakov - - Replaces a lot of magic numbers to enum constants - -2012-11-29 Roman Proskuryakov - - Small fixes - -2012-11-29 Dmitry Kashitsyn - - Refactors object instantination code - -2012-11-29 Dmitry Kashitsyn - - Fixes runtime - -2012-11-29 Dmitry Kashitsyn - - Small fixes - -2012-11-29 Dmitry Kashitsyn - - Fixes memory GC vs VM issues - -2012-11-29 Dmitry Kashitsyn - - Adds logo - -2012-11-29 Dmitry Kashitsyn - - Finor mixes - -2012-11-29 Dmitry Kashitsyn - - More refactoring - -2012-11-29 Dmitry Kashitsyn - - Refactoring and a lot of code style fixes - -2012-11-29 Roman Proskuryakov - - Fix - -2012-11-29 Roman Proskuryakov - - Removes goto - -2012-11-29 Roman Proskuryakov - - Adds BlockReturn opcode - -2012-11-29 Roman Proskuryakov - - Renames variables. Adds comments - -2012-11-29 Dmitry Kashitsyn - - Adds some baker init code - -2012-11-28 Roman Proskuryakov - - Fix - -2012-11-28 Roman Proskuryakov - - Adds sendBinary stub. Adds fixme's - -2012-11-28 Dmitry Kashitsyn - - Fixes linking issues - -2012-11-28 Dmitry Kashitsyn - - Minor fixes - -2012-11-28 Roman Proskuryakov - - Adds ByteArray alloc stub - -2012-11-28 Dmitry Kashitsyn - - Adds heap initialization code - -2012-11-28 Roman Proskuryakov - - Fix. Adds ByteObject clone stub. - -2012-11-28 Roman Proskuryakov - - Fix - -2012-11-28 Dmitry Kashitsyn - - Fixes minor issues. Almost ready for execution\! - -2012-11-28 Dmitry Kashitsyn - - More mm refactoring - -2012-11-28 Dmitry Kashitsyn - - Refactors memory interface and class struct - -2012-11-28 Dmitry Kashitsyn - - Forgets about .kdev4 files - -2012-11-28 Dmitry Kashitsyn - - Fixes typo - -2012-11-28 Dmitry Kashitsyn - - Fixes typos - -2012-11-27 Dmitry Kashitsyn - - Adds more notes to Baker desc - -2012-11-27 Dmitry Kashitsyn - - Adds advanced Baker description - -2012-11-27 Dmitry Kashitsyn - - Adds Baker doc - -2012-11-27 Roman Proskuryakov - - Fix - -2012-11-27 Roman Proskuryakov - - Replaces failPrimitive patterns. - -2012-11-27 Roman Proskuryakov - - Fix. Adds failPrimitive. - -2012-11-27 Roman Proskuryakov - - Adds String:at, String:at:put - -2012-11-27 Roman Proskuryakov - - Fix - -2012-11-27 Roman Proskuryakov - - Adds Array:at - -2012-11-27 Roman Proskuryakov - - Fix - -2012-11-27 Dmitry Kashitsyn - - Fixes minor issues in Baker MM - -2012-11-27 Dmitry Kashitsyn - - Fixes sendUnary - -2012-11-26 Dmitry Kashitsyn - - Backs out ptr<> template stuff - -2012-11-26 Dmitry Kashitsyn - - Adds some more template magic - -2012-11-26 Dmitry Kashitsyn - - Adds template typedefs - -2012-11-26 Dmitry Kashitsyn - - Adds ptr<> template - -2012-11-26 Roman Proskuryakov - - Adds #Array:at:put stub - -2012-11-26 Roman Proskuryakov - - Adds sendUnary opcode - -2012-11-25 Roman Proskuryakov - - Moves SmallInt opcodes into function doSmallInt(...) - -2012-11-25 Roman Proskuryakov - - + opcode bit shift - -2012-11-25 Dmitry Kashitsyn - - Fixes some warnings - -2012-11-25 Dmitry Kashitsyn - - Adds collectGarbage() method. Baker GC is almost done - -2012-11-25 Dmitry Kashitsyn - - Fixes vptr doc - -2012-11-25 Dmitry Kashitsyn - - Adds vptr<> spec to the docs - -2012-11-25 Dmitry Kashitsyn - - Adds Baker garbage collection code (moveObject part) - -2012-11-25 Dmitry Kashitsyn - - Baker MM stub - -2012-11-25 Roman Proskuryakov - - A little fix - -2012-11-25 Roman Proskuryakov - - Adds some primitive opcodes. Fixes some unsinged+signed comparisons' warnings. - -2012-11-24 Roman Proskuryakov - - Fix. Now links. - -2012-11-24 Roman Proskuryakov - - Moves templated method template T* SmalltalkVM::newObject(...) to .h - -2012-11-24 Dmitry Kashitsyn - - Adds startup code stubs - -2012-11-24 Dmitry Kashitsyn - - Adds draft memory interface - -2012-11-24 Dmitry Kashitsyn - - Fix - -2012-11-24 Dmitry Kashitsyn - - More fixes in primitive code - -2012-11-24 Dmitry Kashitsyn - - Fixes primitives code - -2012-11-24 Dmitry Kashitsyn - - Refactors doSpecial handler - -2012-11-24 Dmitry Kashitsyn - - Refactored doSpecial to a separate function - -2012-11-24 Dmitry Kashitsyn - - Removes unnececary trace messages - -2012-11-24 Dmitry Kashitsyn - - Small type fixes; Adds more classdoc - -2012-11-24 Roman Proskuryakov - - + opcodes: - 1) specials - 2) a little bit of primitives - -2012-11-24 Roman Proskuryakov - - Fixes the merge. Now it works. - -2012-11-23 Roman Proskuryakov - - + hgignore - -2012-11-24 Dmitry Kashitsyn - - Removes unnececary type casts - -2012-11-24 Dmitry Kashitsyn - - Refactors TArray as a template class - -2012-11-23 Dmitry Kashitsyn - - Fixes a lot of bugs. Image seems to be working and corrent now - -2012-11-23 Dmitry Kashitsyn - - Some more fixes and testing code - -2012-11-23 Dmitry Kashitsyn - - Fixes readObject() - -2012-11-23 Dmitry Kashitsyn - - Fixes image loading code, adds some debug logs - -2012-11-22 Dmitry Kashitsyn - - Fixes a lot of errors. Now compiles - -2012-11-22 Roman Proskuryakov - - + opcode templates - -2012-11-21 Dmitry Kashitsyn - - Fixes - -2012-11-21 Dmitry Kashitsyn - - Fixes cmake and some of the compilation erors - -2012-11-21 Dmitry Kashitsyn - - Fixes pointer vs reference issue - -2012-11-21 Dmitry Kashitsyn - - Small code refactoring and fixes - -2012-11-21 Roman Proskuryakov - - fix typo - -2012-11-21 Dmitry Kashitsyn - - Adds Image::closeImageFile() - -2012-11-21 Dmitry Kashitsyn - - Adds image loading code - -2012-11-21 Dmitry Kashitsyn - - Adds initial image loading code - -2012-11-21 Dmitry Kashitsyn - - Adds smalltalk syntax highlighting scheme - -2012-11-21 Dmitry Kashitsyn - - Adds draft prototype of exception handling mechanism - -2012-11-20 Dmitry Kashitsyn - - Adds Image source - -2012-11-20 Dmitry Kashitsyn - - Refactors objects logic, adds more VM and Image methods - -2012-11-20 Dmitry Kashitsyn - - Minor changes - -2012-11-19 Dmitry Kashitsyn - - Adds lookupMethodInCache - -2012-11-19 Dmitry Kashitsyn - - Adds more VM logic - -2012-11-19 Dmitry Kashitsyn - - Adds more VM logic - -2012-11-18 Dmitry Kashitsyn - - Adds VM methods (execute) - -2012-11-18 Dmitry Kashitsyn - - Adds lookupMethod stub - -2012-11-18 Dmitry Kashitsyn - - Adds more types; initial VM code - -2012-11-18 Dmitry Kashitsyn - - Initial commit. Adds basic types - From c51976b0eca330ebe26e8c1b1cc6f5b5df2dbf8a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 11 Jun 2015 01:44:01 +0300 Subject: [PATCH 266/290] Makes ChangeLog visible for git diff again Issue: #82 --- .gitattributes | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 4d26d05..d320563 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,2 @@ *man -diff -ChangeLog -diff tests/data/ -diff From 920dcf953fbff8fc81c142919631a518ae88de79 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 11 Jun 2015 01:48:38 +0300 Subject: [PATCH 267/290] Le Roi est mort, vive le Roi! Issue: #82 --- ChangeLog | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/ChangeLog b/ChangeLog index e69de29..362d389 100644 --- a/ChangeLog +++ b/ChangeLog @@ -0,0 +1,152 @@ + +[Current] + +[v_0.4] + +2015-05-20 Vlad + * Add GC log in the format of java gcviewer (#47) + Add class Timer + Add class GCLogger + Add CLI arg to choose GC + Inject GCLogger into GC to trace events +2015-04-19 Dmitry Kashitsyn + * Graph-based API and code generation (#32) + Added basic types for Smalltalk instruction API (TSmalltalkInstruction, BasicBlock, ParsedMethod, ParsedBlock). This API allows easy interaction and navigation within compiled method bytecodes. + + No in-place decoding is needed. Once parsed, method became available for analysis and iteration. + A set of visitor classes is provided for that, such as InstructionVisitor, BasicBlockVisitor, etc. + + The following entities may be easily enumerated and iterated: + - Continious regions of non-terminator instructions within a Smalltalk method (like basic blocks) + - Smalltalk instructions within a single basic block + - Lexical smalltalk blocks within a Smalltalk method (nested blocks are also supported) + - Relations of basic blocks, such as list of basic blocks that jump into current and so on + + * On top of instruction API, high level control graph API is provided. It exploits stack relations of instructions within a method and represents them as a links between individual instruction nodes. + + Therefore, the whole method logic is represented as a graph where nodes are single instructions, links between nodes representing various instruction relations, such as: + - Instruction order within a control flow + - Argument references + - Phi value aggregation + - Conditional and unconditional jumps + + For example, MarkArguments 2 instruction will have two links to the instructions that provide an actual value (even if they're reasonably far away). MarkArguments itself provide a value that is used by the following SendMessage instruction. This allows us to analyze far instruction relations in a method. Thus, each and every instruction node and it's relations represents ALL information required to encode it without knowing any context (like stack position, offsets and so on). + + For visualization and debugging purposes special visualization pass was implemented. GraphVisualizer traverses the ready ControlGraph and outputs nodes and edges in the Graphviz DOT format. Later it can be easily rendered into a raster or vector image using the `dot' utility. + + Finally, graph API is now fully used in JIT VM. New methods are analyzed and their graph is built. Then all codegeneration operations use graph representation of Smalltalk instructions and lower them into corresponding IR. A lot of optimization possibilities are now unlocked: instruction reordering, literal and self calls, compile time type deduction and inference became possible. Some of them already implemented and working. + + Graph representation may answer the question whether a value should be protected by GC root. Using the control graph we may easily say are there dangerous instructions between value producing and consuming nodes. By `dangerous' instruction I mean one that may trigger garbage collection. GC may break unprotected pointer which, for example, may reside in hardware register. So, less protection means less memory accesses and better execution performance. +2015-04-19 Roman Proskuryakov + * Unit tests for graph-based API (#32) + TSmalltalkInstruction + InstructionDecoder + ControlGraph + ABABProblem + StackSemantics + DecodeAllMethods (decode all methods and check whether it fails or not) +2015-04-11 Roman Proskuryakov + * CMake improvements (#61) + Add CTest support + Nice find_package(llvm) + Download and build Google Test Framework if installed is of incorrect arhitecture + Hide CMake variables from user (mark them as advanced) +2015-04-10 Roman Proskuryakov + * Simple man page (#62) + Add description of the project + Describe CLI args +2015-03-26 Roman Proskuryakov + * Integrate Travis CI (#65) + Build Debug, Release, +llvm, -llvm, run tests on Linux + Submit build results to CDash + Submit coverage to Coveralls.io + +[v_0.3] +2015-01-30 Roman Proskuryakov + * Move to LLVM 3.3 (#49) +2015-01-30 Dmitry Kashitsyn + * Fix Baker MM reallocation (#14) +2014-04-09 Roman Proskuryakov + * Production-like project building (#50) + Add CMake check for libreadline, libtinfo, llvm, pthreads, pod2man, gzip + Add target uninstall +2014-03-25 Roman Proskuryakov + * Fix padding when allocate memory (#28) + The code generated page fault during execution of movdqa instruction + This SSE instruction requered the data to be alined by 16 bytes. + The code corresponding to the bug is: + @SmalltalkVM::newOrdinaryObject + for (uint32_t index = 0; index < fieldsCount; index++) + instance->putField(index, globals.nilObject); + movdqa moves a double quadword from src to dst. The compiler optimized this loop into a set of movdqa instructions. + That means that 'instance' was aligned neither by 16 bytes nor by 4 bytes. + When BakerMemoryManager::growHeap does its job it divides newHeapSize by 2. + newHeapSize/2 must be equal to correctPadding(newHeapSize/2). But it is not. +2014-01-08 Roman Proskuryakov + * Optional libreadline (#52) +2014-01-06 Roman Proskuryakov + * Add CPack support (make .deb package, compress and install changelog, fix lintian warnings) (#43) + * Build project on Raspberry Pi, Debian, Ubuntu 64, Mac OS X 10.8, FreeBSD, Cygwin, MinGW (#46) + * Make Image-loadImage cross-platform (#46) + It worked fine for GNU/Linux, FreeBSD, but the is no mmap in MS \|/iNdOwS. + The function is rewritten with std::ifstream. + +2013-11-28 Daniil Burdakov + * Add class TInteger (#51) +2013-11-28 Roman Proskuryakov + * Add CLI args (--help, --version, etc) (#33) + +[v_0.2.1] + +2013-12-01 Roman Proskuryakov + * Fix build on Arch Linux (#45) + +[v_0.2] + +2013-11-17 Roman Proskuryakov + * Add naive implementation of LLVM JIT + * Add stub with examples how to use LLVM +2013-11-17 Dmitry Kashitsyn + * Inject Baker GC into JIT + * Add JIT runtime stats + * Add README.md (#23) + * Fix copyleft notice in all source files (#10) +2013-11-14 Roman Proskuryakov + * Add clang++ support (#39) +2013-11-13 Dmitry Kashitsyn + * Make LLVM optional in CMake build scripts (#9) +2013-11-12 Dmitry Kashitsyn + * Untrack image/LitttleSmalltalk.image by git (#22) +2013-11-08 Roman Proskuryakov + * Reduce the number of C-style casts (#38) +2013-11-06 Roman Proskuryakov + * Add GC that never frees unused memory (#36) +2013-10-31 Roman Proskuryakov + * Fix Smalltalk methods with tests (List>>remove, Array>>sort, Object>><, etc) (#35) +2013-09-30 Roman Proskuryakov + * Fix TDictionary-find caused by bad implementation of binary search (#1, #20, #30) +2013-09-19 Roman Proskuryakov + * Fix Smalltalk Magnitude operator > with tests (#19) + * Remove zeroing memory in TObject constructor (~9% speedup) +2013-09-05 Roman Proskuryakov + * Fix TContext stack overflow caused by incorrect calculation of max stack size +2013-08-05 Roman Proskuryakov + * Add ImageWriter to flush image from memory into file +2013-07-06 Dmitry Kashitsyn + * Add Bison and flex grammar for future implementation of imageBuilder +2013-07-06 Dmitry Kashitsyn + * Add round-robin sheduler to emulate Smalltalk multithreading +2013-07-04 Roman Proskuryakov + * Teach BakerMemoryManager-releaseExternalHeapPointer to remove elements from list by constant time +2013-07-01 Dmitry Kashitsyn + * Add hptr<> to store gc roots using stack objects (massive speedup) +2013-04-26 Dmitry Kashitsyn + * Add GC with generations (broken) +2013-04-18 Dmitry Kashitsyn + * Add console autocompletion using libreadline (#40) + +[v_0.1] +2012-12-28 Dmitry Kashitsyn, Roman Proskuryakov , + * Add first implementation of Smalltalk Virtual Machine + * Add implementation of Baker GC + * Add naive Smalltalk tests From 967d2c4a72e9172ec246ce63b296c45175339d02 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 3 Jun 2015 16:09:19 +0300 Subject: [PATCH 268/290] Fixes codestyle --- include/Timer.h | 74 +++++++++++++++++---------------- include/memory.h | 1 - src/BakerMemoryManager.cpp | 1 - src/GCLogger.cpp | 20 +++++---- src/NonCollectMemoryManager.cpp | 2 +- src/Timer.cpp | 26 ++++++------ 6 files changed, 63 insertions(+), 61 deletions(-) diff --git a/include/Timer.h b/include/Timer.h index f1131b6..3acf321 100644 --- a/include/Timer.h +++ b/include/Timer.h @@ -4,7 +4,7 @@ #include #include -#include +#include using std::stringstream; #include #include @@ -17,7 +17,6 @@ using std::stringstream; #endif - //analogue of c++11 ratio from chrono.h template struct TRatio @@ -40,59 +39,62 @@ enum SuffixMode {SNONE, SSHORT, SFULL}; //analogue of c++11 duration template -class TDuration { +class TDuration +{ private: double value; public: - //argument: duration in target ratio value - TDuration(double duration){ - value = duration; - } - - TDuration() {value = 0;} - - bool isEmpty() { return value == 0;} - - template TDuration convertTo(){ + TDuration() : value(0) { } + TDuration(double duration) : value(duration) { } + + bool isEmpty() const { return value == 0;} + + template TDuration convertTo() const { return TDuration(value * (RATIO::num * RATIO2::den) / (double)(RATIO::den * RATIO2::num)); } - int toInt(){ + int toInt() { return floor(value); } - double toDouble(){ + double toDouble() { return value; } - std::string toString(SuffixMode sMode = SNONE, int symbolsAfterPoint = 0, - const char* pointSymbol = ".", const char* spaceSymbol = " "){ - stringstream ss; + std::string toString( + SuffixMode sMode = SNONE, + int symbolsAfterPoint = 0, + const char* pointSymbol = ".", + const char* spaceSymbol = " " + ) const { + stringstream ss; ss << floor(value); - if(symbolsAfterPoint) - ss << pointSymbol << std::setfill('0') << std::setw(symbolsAfterPoint) + if (symbolsAfterPoint) + ss << pointSymbol << std::setfill('0') << std::setw(symbolsAfterPoint) << floor((value - floor(value)) * pow(10.0, symbolsAfterPoint)); - if(sMode != SNONE) + if (sMode != SNONE) ss << spaceSymbol << getSuffix(sMode); return ss.str(); } - std::string getSuffix(SuffixMode sMode); - - std::ostream& operator<<(std::ostream& os){ - os << toString(); + std::string getSuffix(SuffixMode sMode) const; + + std::ostream& operator<<(std::ostream& os) const { + os << this->toString(); return os; } - bool operator< (const TDuration& rhs){ + bool operator< (const TDuration& rhs) const { return value < rhs.value; } - TDuration operator+(const TDuration& rhs){ - return TDuration(value + rhs.value); + TDuration operator+(const TDuration& rhs) const { + return TDuration(value + rhs.value); } - TDuration operator-(const TDuration& rhs){ - return TDuration(value - rhs.value); + TDuration operator-(const TDuration& rhs) const { + return TDuration(value - rhs.value); } - bool operator> (const TDuration& rhs){return !(operator< (rhs));} + bool operator> (const TDuration& rhs) const { + return !(operator< (rhs)); // FIXME: it works as >= + } friend class Timer; }; @@ -100,17 +102,17 @@ class TDuration { class Timer { private: - systemTimeValue timeCreate; - double getDiffSec(); + systemTimeValue timeCreate; + double getDiffSec() const; public: //timer which count time from specified unix-time. Timer(time_t time); //default constructor is implicit Timer::now() - Timer(){ this->start();} - static Timer now() {Timer t; return t;} + Timer(){ this->start(); } + static Timer now() { return Timer(); } void start(); template - TDuration get(){ + TDuration get() const { return TDuration(getDiffSec()).convertTo(); } }; diff --git a/include/memory.h b/include/memory.h index 41b505f..e4d914d 100644 --- a/include/memory.h +++ b/include/memory.h @@ -523,4 +523,3 @@ class Image::ImageWriter }; #endif - diff --git a/src/BakerMemoryManager.cpp b/src/BakerMemoryManager.cpp index fad1c4a..da0094e 100644 --- a/src/BakerMemoryManager.cpp +++ b/src/BakerMemoryManager.cpp @@ -530,4 +530,3 @@ TMemoryManagerInfo BakerMemoryManager::getStat() { return m_memoryInfo; } - diff --git a/src/GCLogger.cpp b/src/GCLogger.cpp index e80b9ca..8aa5c46 100644 --- a/src/GCLogger.cpp +++ b/src/GCLogger.cpp @@ -5,35 +5,37 @@ GCLogger::GCLogger(const char* fileName): m_logFile(fileName, std::fstream::out) {} -GCLogger::~GCLogger(){ +GCLogger::~GCLogger() { m_logFile.flush(); } enum MeasuringConstants { bytes_in_kb = 1024 }; -void GCLogger::writeLogLine(TMemoryManagerEvent event){ +void GCLogger::writeLogLine(TMemoryManagerEvent event) { m_logFile << event.begin.toString(SNONE, 3) << ": [" << event.eventName << " "; - if(!event.heapInfo.empty()){ + if (!event.heapInfo.empty()) + { TMemoryManagerHeapInfo eh = event.heapInfo; m_logFile << eh.usedHeapSizeBeforeCollect / bytes_in_kb << "K->" << eh.usedHeapSizeAfterCollect / bytes_in_kb << "K(" << eh.totalHeapSize / bytes_in_kb << "K)"; - for(std::list::iterator i = eh.heapEvents.begin(); i != eh.heapEvents.end(); i++){ + for (std::list::iterator i = eh.heapEvents.begin(); i != eh.heapEvents.end(); i++) { m_logFile << "[" << i->eventName << ": " << i->usedHeapSizeBeforeCollect / bytes_in_kb << "K->" << i->usedHeapSizeAfterCollect / bytes_in_kb << "K(" << i->totalHeapSize / bytes_in_kb << "K)"; - if(!i->timeDiff.isEmpty()) + if ( !i->timeDiff.isEmpty() ) m_logFile << ", " << i->timeDiff.toString(SSHORT, 6); m_logFile << "] "; } } - if(!event.timeDiff.isEmpty()) + if(!event.timeDiff.isEmpty()) { m_logFile << ", " << event.timeDiff.toString(SSHORT, 6); - //gc-viewer see error when no delay or delay is 0.0 - else - m_logFile << ", 0.000001 secs"; + } else { + // gc-viewer reports error when there is no delay or delay is 0.0 + m_logFile << ", 0.000001 secs"; + } m_logFile << "]\n"; } diff --git a/src/NonCollectMemoryManager.cpp b/src/NonCollectMemoryManager.cpp index 74f30fd..9a9e389 100644 --- a/src/NonCollectMemoryManager.cpp +++ b/src/NonCollectMemoryManager.cpp @@ -45,7 +45,7 @@ NonCollectMemoryManager::NonCollectMemoryManager() : - m_memoryInfo(), m_heapSize(0), m_heapBase(0), m_heapPointer(0), + m_memoryInfo(), m_heapSize(0), m_heapBase(0), m_heapPointer(0), m_staticHeapSize(0), m_staticHeapBase(0), m_staticHeapPointer(0) {} diff --git a/src/Timer.cpp b/src/Timer.cpp index 20e60e9..ec5ed84 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -6,7 +6,7 @@ using std::stringstream; #if defined(unix) || defined(__unix__) || defined(__unix) -Timer::Timer(time_t _time){ +Timer::Timer(time_t _time) { time_t current; time(¤t); time_t diff = current - _time; @@ -16,11 +16,11 @@ Timer::Timer(time_t _time){ timeCreate = result; } -void Timer::start(){ +void Timer::start() { gettimeofday(&timeCreate, 0); } -double Timer::getDiffSec(){ +double Timer::getDiffSec() const { timeval current; gettimeofday(¤t, 0); double diff = current.tv_sec + current.tv_usec/static_cast(TMicrosec::den) @@ -28,7 +28,7 @@ double Timer::getDiffSec(){ return diff; } #else -Timer::Timer(time_t _time){ +Timer::Timer(time_t _time) { time_t current; time(¤t); clock_t diff = (current - _time)*CLOCKS_PER_SEC; @@ -36,34 +36,34 @@ Timer::Timer(time_t _time){ } -void Timer::start(){ +void Timer::start() { timeCreate = clock(); } -double Timer::getDiffSec(){ +double Timer::getDiffSec() { return static_cast((clock() - timeCreate))/CLOCKS_PER_SEC; } #endif -template <> std::string TDuration::getSuffix(SuffixMode sMode){ +template <> std::string TDuration::getSuffix(SuffixMode sMode) const { return sMode == SFULL ? "days" : sMode == SSHORT ? "days" : ""; } -template <> std::string TDuration::getSuffix(SuffixMode sMode){ +template <> std::string TDuration::getSuffix(SuffixMode sMode) const { return sMode == SFULL ? "hours" : sMode == SSHORT ? "hours" : ""; } -template <> std::string TDuration::getSuffix(SuffixMode sMode){ +template <> std::string TDuration::getSuffix(SuffixMode sMode) const { return sMode == SFULL ? "minutes" : sMode == SSHORT ? "mins" : ""; } -template <> std::string TDuration::getSuffix(SuffixMode sMode){ +template <> std::string TDuration::getSuffix(SuffixMode sMode) const { return sMode == SFULL ? "seconds" : sMode == SSHORT ? "secs" : ""; } -template <> std::string TDuration::getSuffix(SuffixMode sMode){ +template <> std::string TDuration::getSuffix(SuffixMode sMode) const { return sMode == SFULL ? "milliseconds" : sMode == SSHORT ? "msecs" : ""; } -template <> std::string TDuration::getSuffix(SuffixMode sMode){ +template <> std::string TDuration::getSuffix(SuffixMode sMode) const { return sMode == SFULL ? "microseconds" : sMode == SSHORT ? "mcsecs" : ""; } -template <> std::string TDuration::getSuffix(SuffixMode sMode){ +template <> std::string TDuration::getSuffix(SuffixMode sMode) const { return sMode == SFULL ? "nanoseconds" : sMode == SSHORT ? "usecs" : ""; } From a92e7aa83c9e2e49217aa08c6584593635a66c2c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 3 Jun 2015 16:09:34 +0300 Subject: [PATCH 269/290] Adds description about --mm_type into --help --- src/args.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/args.cpp b/src/args.cpp index f9a4f95..a3c37e2 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -122,6 +122,7 @@ std::string args::getHelp() " -h, --heap Starting of the heap in bytes\n" " -H, --heap_max Maximum allowed heap size\n" " -i, --image Path to image\n" + " --mm_type arg (=copy) Choose memory manager. nc - NonCollect, copy - Stop-and-Copy\n" " -V, --version Display the version number and copyrights of the invoked LLST\n" " --help Display this information and quit"; } From de0b10b6072d50d97d4bcd943d64f7fb2e1db30d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 3 Jun 2015 16:11:55 +0300 Subject: [PATCH 270/290] Removes garbage from main --- src/main.cpp | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a7b4b12..2429f47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,18 +49,6 @@ #include -void testControlGraph(Image* image) { - TClass* objectClass = image->getGlobal("Object"); - TMethod* isKindOfMethod = objectClass->methods->find("isKindOf:"); - - st::ParsedMethod parsedMethod(isKindOfMethod); - st::ControlGraph controlGraph(&parsedMethod); - controlGraph.buildGraph(); - - ControlGraphVisualizer visualizer(&controlGraph, "graph"); - visualizer.run(); -} - int main(int argc, char **argv) { args llstArgs; @@ -104,12 +92,6 @@ int main(int argc, char **argv) { std::auto_ptr smalltalkImage(new Image(memoryManager.get())); smalltalkImage->loadImage(llstArgs.imagePath); - testControlGraph(smalltalkImage.get()); - -// { -// Image::ImageWriter writer; -// writer.setGlobals(globals).writeTo("../image/MySmalltalkImage.image"); -// } SmalltalkVM vm(smalltalkImage.get(), memoryManager.get()); // Creating completion database and filling it with info @@ -146,8 +128,6 @@ int main(int argc, char **argv) { // And starting the image execution! SmalltalkVM::TExecuteResult result = vm.execute(initProcess, 0); - //llvm::outs() << *runtime.getModule(); - /* This code will run Smalltalk immediately in LLVM. * Don't forget to uncomment 'Undefined>>boot' */ @@ -156,6 +136,7 @@ int main(int argc, char **argv) { TExecuteProcessFunction executeProcess = reinterpret_cast(runtime.getExecutionEngine()->getPointerToFunction(runtime.getModule()->getFunction("executeProcess"))); SmalltalkVM::TExecuteResult result = (SmalltalkVM::TExecuteResult) executeProcess(initProcess); */ + // Finally, parsing the result switch (result) { case SmalltalkVM::returnError: From 7a1536a47431f7d45025adb3a338663aa7ff9dce Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 3 Jun 2015 16:36:53 +0300 Subject: [PATCH 271/290] Removes trash comments from ControlGraphVisualizer --- src/ControlGraphVisualizer.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 6aded5b..984bd27 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -40,13 +40,7 @@ ControlGraphVisualizer::ControlGraphVisualizer(st::ControlGraph* graph, const st } bool ControlGraphVisualizer::visitDomain(st::ControlDomain& /*domain*/) { -// if (!firstDomain) -// m_stream << "\t} \n" << std::endl; // closing subgraph - firstDomain = false; - -// m_stream << "\n\tsubgraph cluster_" << domain.getBasicBlock()->getOffset() << " {\n"; -// return st::NodeVisitor::visitDomain(domain); return false; } @@ -113,9 +107,6 @@ bool ControlGraphVisualizer::visitNode(st::ControlNode& node) { m_stream << "\t\t" << incoming.domain->getTerminator()->getIndex() << " -> " << phi->getIndex() << " [" << "style=\"invis\" constraint=true ];\n"; - -// m_stream << "\t\t" << node.getIndex() << " -> " << incoming.value->getIndex() << " [" -// << " labelfloat=true color=\"blue\" fontcolor=\"blue\" style=\"dashed\" constraint=false ];\n"; } } @@ -191,9 +182,6 @@ void ControlGraphVisualizer::markNode(st::ControlNode* node) { } void ControlGraphVisualizer::finish() { -// if (!firstDomain) -// m_stream << "\t} \n"; - m_stream << "} \n"; m_stream.close(); } From 84d98aa44af2c3a209090431cbafe556a29cf23a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 3 Jun 2015 16:37:13 +0300 Subject: [PATCH 272/290] Removes trash comments from MethodCompiler --- src/MethodCompiler.cpp | 64 ++---------------------------------------- 1 file changed, 2 insertions(+), 62 deletions(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 4291ec0..3b8f2c8 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -92,7 +92,6 @@ Function* MethodCompiler::createFunction(TMethod* method) function->setCallingConv(CallingConv::C); //Anyway C-calling conversion is default function->setGC("shadow-stack"); function->addFnAttr(Attribute::InlineHint); -// function->addFnAttr(Attribute::AlwaysInline); return function; } @@ -234,18 +233,12 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) while (true) { if (node == consumer) { -// outs() << "Producer " << producer->getIndex() << " is safe and do not need a protection (1)\n"; - return false; } assert(node->getNodeType() == st::ControlNode::ntInstruction); st::InstructionNode* const candidate = node->cast(); if (candidate->getInstruction().mayCauseGC()) { -// outs() << "Producer " << producer->getIndex() -// << " should be protected because node " -// << candidate->getIndex() << " may cause GC\n"; - return true; } @@ -273,14 +266,10 @@ bool MethodCompiler::shouldProtectProducer(st::ControlNode* producer) walker.run(consumer, st::GraphWalker::wdBackward); if (detector.isDetected()) { -// outs() << "Producer " << producer->getIndex() -// << " should be protected because detector says that it is required\n"; - return true; } } -// outs() << "Producer " << producer->getIndex() << " is safe and do not need a protection (2)\n"; return false; } @@ -367,7 +356,6 @@ void MethodCompiler::scanForBranches(TJITContext& jit, st::ParsedBytecode* sourc m_jit.function // method's function ); -// compiler->m_targetToBlockMap[basicBlock.getOffset()] = newBlock; basicBlock.setValue(newBlock); return true; @@ -408,13 +396,6 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Creating the function named as "Class>>method" or using provided one jit.function = methodFunction ? methodFunction : createFunction(method); -// outs() << "Compiling " << jit.function->getName() << "\n"; - -// { -// ControlGraphVisualizer vis(jit.controlGraph, jit.function->getName(), "dots/"); -// vis.run(); -// } - // Creating the preamble basic block and inserting it into the function // It will contain basic initialization code (args, temps and so on) jit.preamble = BasicBlock::Create(m_JITModule->getContext(), "preamble", jit.function); @@ -462,9 +443,6 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Cleaning up m_blockFunctions.clear(); -// m_targetToBlockMap.clear(); - -// outs() << "Done compiling method " << jit.function->getName() << "\n"; return jit.function; } @@ -478,7 +456,6 @@ void MethodCompiler::writeFunctionBody(TJITContext& jit) private: virtual bool visitDomain(st::ControlDomain& domain) { llvm::BasicBlock* newBlock = domain.getBasicBlock()->getValue(); - // llvm::BasicBlock* newBlock = m_jit.compiler->m_targetToBlockMap[domain.getBasicBlock()->getOffset()]; newBlock->moveAfter(m_jit.builder->GetInsertBlock()); // for a pretty sequenced BB output m_jit.builder->SetInsertPoint(newBlock, newBlock->getFirstInsertionPt()); @@ -708,19 +685,10 @@ void MethodCompiler::doPushBlock(TJITContext& jit) ss << jit.originMethod->klass->name->toString() + ">>" + jit.originMethod->name->toString() << "@" << blockOffset; std::string blockFunctionName = ss.str(); -// outs() << "Compiling block " << blockFunctionName << "\n"; - -// { -// ControlGraphVisualizer vis(blockContext.controlGraph, blockFunctionName, "dots/"); -// vis.run(); -// } - // If block function is not already created, create it if (! m_JITModule->getFunction(blockFunctionName)) compileBlock(jit, blockFunctionName, parsedBlock); -// outs() << "Done compiling block " << blockFunctionName << "\n"; - // Create block object and fill it with context information Value* const args[] = { jit.getCurrentContext(), // creatingContext @@ -780,7 +748,7 @@ llvm::Function* MethodCompiler::compileBlock(TJITContext& jit, const std::string std::stringstream ss; ss.str(""); ss << "offset" << blockOffset; - BasicBlock* const blockBody = parsedBlock->getBasicBlockByOffset(blockOffset)->getValue(); // m_targetToBlockMap[blockOffset]; + BasicBlock* const blockBody = parsedBlock->getBasicBlockByOffset(blockOffset)->getValue(); assert(blockBody); blockBody->setName(ss.str()); @@ -789,13 +757,9 @@ llvm::Function* MethodCompiler::compileBlock(TJITContext& jit, const std::string writeFunctionBody(blockContext); - // outs() << *blockContext.function << "\n"; - // Running optimization passes on a block function JITRuntime::Instance()->optimizeFunction(blockContext.function, false); - // outs() << *blockContext.function << "\n"; - return blockContext.function; } @@ -842,15 +806,12 @@ void MethodCompiler::doMarkArguments(TJITContext& jit) jit.builder->CreateCall3(m_baseFunctions.setObjectField, argumentsObject, jit.builder->getInt32(--index), argument); } Value* const argumentsArray = jit.builder->CreateBitCast(argumentsObject, m_baseTypes.objectArray->getPointerTo()); - //Value* const argsHolder = protectProducerNode(jit, jit.currentNode, argumentsArray); - //argsHolder->setName("pArgs."); Value* const argumentsPointer = jit.builder->CreateBitCast(argumentsObject, jit.builder->getInt8PtrTy()); Function* gcrootIntrinsic = getDeclaration(m_JITModule, Intrinsic::lifetime_start); jit.builder->CreateCall2(gcrootIntrinsic, jit.builder->getInt64(sizeInBytes), argumentsPointer); setNodeValue(jit, jit.currentNode, argumentsArray); - // jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, argsHolder)); } void MethodCompiler::doSendUnary(TJITContext& jit) @@ -869,7 +830,6 @@ void MethodCompiler::doSendUnary(TJITContext& jit) // FIXME Do not protect the result object because it will always be the literal value Value* const result = jit.builder->CreateSelect(condition, m_globals.trueObject, m_globals.falseObject); setNodeValue(jit, jit.currentNode, result); - //jit.pushValue(result); } llvm::Value* MethodCompiler::getArgument(TJITContext& jit, std::size_t index/* = 0*/) { @@ -993,8 +953,6 @@ void MethodCompiler::doSendBinary(TJITContext& jit) BasicBlock* const sendBinaryBlock = BasicBlock::Create(m_JITModule->getContext(), "asObjects.", jit.function); BasicBlock* const resultBlock = BasicBlock::Create(m_JITModule->getContext(), "result.", jit.function); -// jit.currentNode->getDomain()->getBasicBlock()->setEndValue(resultBlock); - // Depending on the contents we may either do the integer operations // directly or create a send message call using operand objects jit.builder->CreateCondBr(isSmallInts, integersBlock, sendBinaryBlock); @@ -1083,7 +1041,6 @@ void MethodCompiler::doSendBinary(TJITContext& jit) Value* const resultHolder = protectProducerNode(jit, jit.currentNode, phi); setNodeValue(jit, jit.currentNode, resultHolder); - //jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } @@ -1275,10 +1232,6 @@ void MethodCompiler::doSendMessage(TJITContext& jit) if (instruction.getOpcode() == opcode::pushArgument && instruction.getArgument() == 0) { TClass* const receiverClass = jit.originMethod->klass; - -// TODO if (sendToSuper) -// receiverClass = receiverClass->parentClass; - if (receiverClass->package == globals.nilObject) { if (doSendMessageToLiteral(jit, receiverNode, receiverClass)) return; @@ -1330,31 +1283,22 @@ void MethodCompiler::doSendMessage(TJITContext& jit) Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); setNodeValue(jit, jit.currentNode, resultHolder); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } void MethodCompiler::doSpecial(TJITContext& jit) { const uint8_t opcode = jit.currentNode->getInstruction().getArgument(); -// BasicBlock::iterator iPreviousInst = jit.builder->GetInsertPoint(); -// if (iPreviousInst != jit.builder->GetInsertBlock()->begin()) -// --iPreviousInst; - switch (opcode) { case special::selfReturn: -// if (! iPreviousInst->isTerminator()) jit.builder->CreateRet(jit.getSelf()); break; case special::stackReturn: -// if ( !iPreviousInst->isTerminator() && jit.hasValue() ) jit.builder->CreateRet(getArgument(jit)); // jit.popValue()); break; - case special::blockReturn: - /*if ( !iPreviousInst->isTerminator() && jit.hasValue())*/ { - // Peeking the return value from the stack + case special::blockReturn: { Value* const value = getArgument(jit); // jit.popValue(); // Loading the target context information @@ -1444,7 +1388,6 @@ void MethodCompiler::doSpecial(TJITContext& jit) Value* const result = jit.builder->CreateCall(m_runtimeAPI.sendMessage, sendMessageArgs); Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); setNodeValue(jit, jit.currentNode, resultHolder); -// jit.pushValue(new TDeferredValue(&jit, TDeferredValue::loadHolder, resultHolder)); } break; default: @@ -1505,9 +1448,6 @@ void MethodCompiler::doPrimitive(TJITContext& jit) // FIXME Are we really allowed to use the value without holder? setNodeValue(jit, jit.currentNode, m_globals.nilObject); -// jit.currentNode->getDomain()->getBasicBlock()->setEndValue(primitiveFailedBB); - -// jit.pushValue(m_globals.nilObject); } From 8569bdd87a5b4130219bdc5cf26a38ba8cf4d3d5 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 4 Jun 2015 03:24:29 +0300 Subject: [PATCH 273/290] Fixes library linkage order --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d29ef55..64b601d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,12 +141,12 @@ if (USE_LLVM) endif() add_executable(llst src/main.cpp src/vm.cpp) -target_link_libraries(llst standart_set memory_managers stapi ${READLINE_LIBS_TO_LINK} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) add_dependencies(llst image) if (USE_LLVM) target_link_libraries(llst jit ${LLVM_LIBS} ${LLVM_LD_FLAGS}) endif() +target_link_libraries(llst standart_set memory_managers stapi ${READLINE_LIBS_TO_LINK} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) set(changelog_compressed "${CMAKE_CURRENT_BINARY_DIR}/changelog.gz") gzip_compress("compress_changelog" "${CMAKE_CURRENT_SOURCE_DIR}/ChangeLog" ${changelog_compressed}) From 7e948f216f7b11449688b07373141097c7fca3c4 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 7 Jun 2015 22:07:55 +0300 Subject: [PATCH 274/290] Makes more functions in Timer.h be const --- include/Timer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Timer.h b/include/Timer.h index 3acf321..c726b82 100644 --- a/include/Timer.h +++ b/include/Timer.h @@ -52,10 +52,10 @@ class TDuration template TDuration convertTo() const { return TDuration(value * (RATIO::num * RATIO2::den) / (double)(RATIO::den * RATIO2::num)); } - int toInt() { + int toInt() const { return floor(value); } - double toDouble() { + double toDouble() const { return value; } std::string toString( From d432ee2f49a75c1b39b4363f6a6f9ae95e152d93 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 10 Jun 2015 00:31:05 +0300 Subject: [PATCH 275/290] Fixes outdated comments with m_targetToBlockMap --- include/jit.h | 1 - src/MethodCompiler.cpp | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/jit.h b/include/jit.h index 54f5341..49ff0bd 100644 --- a/include/jit.h +++ b/include/jit.h @@ -262,7 +262,6 @@ class MethodCompiler { private: JITRuntime& m_runtime; llvm::Module* m_JITModule; -// std::map m_targetToBlockMap; void scanForBranches(TJITContext& jit, st::ParsedBytecode* source, uint32_t byteCount = 0); bool scanForBlockReturn(TJITContext& jit, uint32_t byteCount = 0); diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 3b8f2c8..ea21d39 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -338,7 +338,7 @@ bool MethodCompiler::scanForBlockReturn(TJITContext& jit, uint32_t byteCount/* = void MethodCompiler::scanForBranches(TJITContext& jit, st::ParsedBytecode* source, uint32_t byteCount /*= 0*/) { // Iterating over method's basic blocks and creating their representation in LLVM - // Created blocks are collected in the m_targetToBlockMap map with bytecode offset as a key + // Created blocks are used to map bytecode BBs and LLVM BBs class Visitor : public st::BasicBlockVisitor { public: @@ -423,12 +423,10 @@ Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodF // Scans the bytecode for the branch sites and // collects branch targets. Creates target basic blocks beforehand. - // Target blocks are collected in the m_targetToBlockMap map with - // target bytecode offset as a key. scanForBranches(jit, jit.parsedMethod); // Switching builder context to the first basic block from the preamble - BasicBlock* const body = jit.parsedMethod->getBasicBlockByOffset(0)->getValue(); // m_targetToBlockMap[0]; + BasicBlock* const body = jit.parsedMethod->getBasicBlockByOffset(0)->getValue(); assert(body); body->setName("offset0"); @@ -919,7 +917,7 @@ void MethodCompiler::encodePhiIncomings(TJITContext& jit, st::PhiNode* phiNode) for (std::size_t index = 0; index < incomingList.size(); index++) { const st::PhiNode::TIncoming& incoming = incomingList[index]; - BasicBlock* const incomingBlock = incoming.domain->getBasicBlock()->getEndValue(); // m_targetToBlockMap[incoming.domain->getBasicBlock()->getOffset()]; + BasicBlock* const incomingBlock = incoming.domain->getBasicBlock()->getEndValue(); assert(incomingBlock); // This call may change the insertion point if one of the incoming values is a value holder, @@ -1336,7 +1334,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) // Finding appropriate branch target // from the previously stored basic blocks - BasicBlock* const target = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(targetOffset)->getValue(); // m_targetToBlockMap[targetOffset]; + BasicBlock* const target = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(targetOffset)->getValue(); assert(target); jit.builder->CreateBr(target); @@ -1350,11 +1348,11 @@ void MethodCompiler::doSpecial(TJITContext& jit) // Finding appropriate branch target // from the previously stored basic blocks - BasicBlock* const targetBlock = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(targetOffset)->getValue(); // m_targetToBlockMap[targetOffset]; + BasicBlock* const targetBlock = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(targetOffset)->getValue(); // This is a block that goes right after the branch instruction. // If branch condition is not met execution continues right after - BasicBlock* const skipBlock = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(skipOffset)->getValue(); // m_targetToBlockMap[skipOffset]; + BasicBlock* const skipBlock = jit.controlGraph->getParsedBytecode()->getBasicBlockByOffset(skipOffset)->getValue(); // Creating condition check Value* const boolObject = (opcode == special::branchIfTrue) ? m_globals.trueObject : m_globals.falseObject; From 8405f98bed3be18cb59441a5d2076ceb748dabc7 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 10 Jun 2015 00:38:24 +0300 Subject: [PATCH 276/290] Fixes comments in JITRuntime.cpp --- src/JITRuntime.cpp | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index 6398cf1..b330cd9 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -310,19 +310,14 @@ TObject* JITRuntime::invokeBlock(TBlock* block, TContext* callingContext, bool o // If function was not found then the whole method needs compilation. // Compiling function and storing it to the table for further use - -// Function* methodFunction = m_methodCompiler->compileMethod(block->method); -// blockFunction = m_JITModule->getFunction(blockFunctionName); blockFunction = m_methodCompiler->compileBlock(block); - if (/*!methodFunction ||*/ !blockFunction) { + if (!blockFunction) { // Something is really wrong! outs() << "JIT: Fatal error in invokeBlock for " << blockFunctionName << "\n"; std::exit(1); } -// outs() << *blockFunction << "\n"; - verifyModule(*m_JITModule, AbortProcessAction); optimizeFunction(blockFunction, true); @@ -380,8 +375,6 @@ TObject* JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TOb // Compiling function and storing it to the table for further use methodFunction = m_methodCompiler->compileMethod(method); -// outs() << *methodFunction << "\n"; - verifyModule(*m_JITModule, AbortProcessAction); optimizeFunction(methodFunction, true); @@ -433,9 +426,6 @@ void JITRuntime::updateHotSites(TMethodFunction methodFunction, TContext* callin if (!callSiteIndex) return; -// if (callingContext->getClass() == globals.blockClass) -// static_cast(callingContext)->; - TMethodFunction callerMethodFunction = lookupFunctionInCache(callingContext->method); // TODO reload cache if callerMethodFunction was popped out @@ -502,8 +492,6 @@ void JITRuntime::patchHotMethods() ++iSite; } -// outs() << "Patched code: \n" << *hotMethod->methodFunction << "\n"; - outs() << "done. Verifying ..."; verifyModule(*m_JITModule, AbortProcessAction); @@ -532,8 +520,6 @@ void JITRuntime::patchHotMethods() verifyModule(*m_JITModule, AbortProcessAction); -// outs() << "Optimized code: \n" << *hotMethod->methodFunction; - outs() << "done.\n"; } @@ -557,9 +543,6 @@ void JITRuntime::patchHotMethods() outs() << "Compiling machine code for " << hotMethod->methodFunction->getName().str() << " ..."; m_executionEngine->recompileAndRelinkFunction(hotMethod->methodFunction); - -// outs() << "Final code: \n" << *hotMethod->methodFunction; - outs() << "done.\n"; } @@ -965,7 +948,7 @@ void JITRuntime::initializePassManager() { m_functionPassManager->add(createDeadStoreEliminationPass()); m_functionPassManager->add(createLLSTPass()); // FIXME direct calls break the logic -// //If llstPass removed GC roots, we may try DCE again + //If llstPass removed GC roots, we may try DCE again m_functionPassManager->add(createDeadCodeEliminationPass()); m_functionPassManager->add(createDeadStoreEliminationPass()); From c6ff0ac4d3e0eb0db868afdbb79b8f6f0ff9aec6 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 10 Jun 2015 01:31:44 +0300 Subject: [PATCH 277/290] Fixes llvm version in README and shows how to build unit tests --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 084f020..258aa83 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,15 @@ By default LLST is built without LLVM support. If you wish to enable it, you sho ~/llst/build $ make ``` -You should have LLVM 3.1 installed and llvm-config or llvm-config-3.1 be accessible from your environment. +You should have LLVM 3.3 installed and llvm-config or llvm-config-3.3 be accessible from your environment. + +Unit tests +==== + +``` +~/llst/build $ cmake -DBUILD_TESTS=ON .. +~/llst/build $ make check +``` License ======= From 40be96b8245ac695fa56271a8f7151625b7b5148 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 11 Jun 2015 02:02:21 +0300 Subject: [PATCH 278/290] Adds --mm_type into man page --- doc/llst.1.en.pod | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/llst.1.en.pod b/doc/llst.1.en.pod index 4a2a78e..190f64b 100644 --- a/doc/llst.1.en.pod +++ b/doc/llst.1.en.pod @@ -37,6 +37,10 @@ FIXME: Provide imageBuilder with the package (you may find the binary in sources Max size of the VM heap. VM will not increase the size of the heap if maximum size is reached. +=item B<--mm_type=>type + + Choose memory manager. nc - NonCollect, copy - Stop-and-Copy. Default is copy. + =item B<--help> Display short help and quit From 6fa309d6a901a4e3617f639e011793b77bf7fdf5 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 9 Aug 2015 05:59:41 +0300 Subject: [PATCH 279/290] Bumps version upto 0.4 --- LICENSE | 2 +- include/CompletionEngine.h | 2 +- include/Core.ll | 2 +- include/args.h | 2 +- include/ib.h | 2 +- include/jit.h | 2 +- include/llstDebuggingPass.h | 2 +- include/llstPass.h | 2 +- include/memory.h | 2 +- include/opcodes.h | 2 +- include/primitives.h | 2 +- include/types.h | 2 +- include/vm.h | 2 +- src/BakerMemoryManager.cpp | 2 +- src/CompletionEngine.cpp | 2 +- src/GenerationalMemoryManager.cpp | 2 +- src/Image.cpp | 2 +- src/JITRuntime.cpp | 2 +- src/LLVMMemoryManager.cpp | 2 +- src/MethodCompiler.cpp | 2 +- src/NonCollectMemoryManager.cpp | 2 +- src/TDictionary.cpp | 2 +- src/TSymbol.cpp | 2 +- src/args.cpp | 2 +- src/llstDebuggingPass.cpp | 2 +- src/llstPass.cpp | 2 +- src/main.cpp | 2 +- src/primitives.cpp | 2 +- src/vm.cpp | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/LICENSE b/LICENSE index c615b23..2e9814a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -LLVM Smalltalk or Lo Level Smalltalk or LLST, Version 0.1 +LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 Copyright (C) 2012 by Dmitry Kashitsyn aka Korvin aka Halt Copyright (C) 2012 by Roman Proskuryakov aka Humbug diff --git a/include/CompletionEngine.h b/include/CompletionEngine.h index e78d5e0..dbda3c0 100644 --- a/include/CompletionEngine.h +++ b/include/CompletionEngine.h @@ -3,7 +3,7 @@ * * Console completion proposals engine * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/Core.ll b/include/Core.ll index 44f49f0..2564243 100644 --- a/include/Core.ll +++ b/include/Core.ll @@ -9,7 +9,7 @@ ; small and almost all of them gets inlined, so it does not ; affect the perfomance of JIT code. ; -; LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 +; LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 ; ; LLST is ; Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/args.h b/include/args.h index ce8b4a1..ff4741c 100644 --- a/include/args.h +++ b/include/args.h @@ -3,7 +3,7 @@ * * Helper functions for command line argument parsing * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/ib.h b/include/ib.h index 2729e41..d9ca80c 100644 --- a/include/ib.h +++ b/include/ib.h @@ -3,7 +3,7 @@ * * Interface for the native method compiler * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/jit.h b/include/jit.h index 49ff0bd..31816b1 100644 --- a/include/jit.h +++ b/include/jit.h @@ -3,7 +3,7 @@ * * LLVM related routines * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/llstDebuggingPass.h b/include/llstDebuggingPass.h index 5d5be6a..9788987 100644 --- a/include/llstDebuggingPass.h +++ b/include/llstDebuggingPass.h @@ -3,7 +3,7 @@ * * Debugging LLVM pass * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/llstPass.h b/include/llstPass.h index af13767..69fa552 100644 --- a/include/llstPass.h +++ b/include/llstPass.h @@ -4,7 +4,7 @@ * Optimizing LLVM pass for JIT code generator. * Tries to remove redundant memory loads and roots. * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/memory.h b/include/memory.h index e4d914d..ceee7c4 100644 --- a/include/memory.h +++ b/include/memory.h @@ -3,7 +3,7 @@ * * LLST memory management routines and interfaces * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/opcodes.h b/include/opcodes.h index 554e324..8a6bd01 100644 --- a/include/opcodes.h +++ b/include/opcodes.h @@ -3,7 +3,7 @@ * * Instruction codes of the Smalltalk virtual machine * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/primitives.h b/include/primitives.h index 892082e..46fcaf3 100644 --- a/include/primitives.h +++ b/include/primitives.h @@ -3,7 +3,7 @@ * * Primitive handling functions which are part of soft VM * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/types.h b/include/types.h index b4a78e0..2a8190d 100644 --- a/include/types.h +++ b/include/types.h @@ -3,7 +3,7 @@ * * Basic Smalltalk related types and structures * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/include/vm.h b/include/vm.h index 658cdf7..31c1201 100644 --- a/include/vm.h +++ b/include/vm.h @@ -3,7 +3,7 @@ * * LLST virtual machine related classes and structures * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/BakerMemoryManager.cpp b/src/BakerMemoryManager.cpp index da0094e..27f78fa 100644 --- a/src/BakerMemoryManager.cpp +++ b/src/BakerMemoryManager.cpp @@ -3,7 +3,7 @@ * * Implementation of BakerMemoryManager class * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/CompletionEngine.cpp b/src/CompletionEngine.cpp index e01cf56..435be51 100644 --- a/src/CompletionEngine.cpp +++ b/src/CompletionEngine.cpp @@ -3,7 +3,7 @@ * * Smalltalk aware completion functions for readline library * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/GenerationalMemoryManager.cpp b/src/GenerationalMemoryManager.cpp index 630c2f8..89d6545 100644 --- a/src/GenerationalMemoryManager.cpp +++ b/src/GenerationalMemoryManager.cpp @@ -5,7 +5,7 @@ * original Baker memory manager by introducing asymmetrical * handling of heap parts. * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/Image.cpp b/src/Image.cpp index 0d383fc..c5d6f89 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -3,7 +3,7 @@ * * Implementation of Image class which loads the file image into memory * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index b330cd9..2d86e9b 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -3,7 +3,7 @@ * * LLST Runtime environment * -* LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 +* LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/LLVMMemoryManager.cpp b/src/LLVMMemoryManager.cpp index 98704ae..e43b60e 100644 --- a/src/LLVMMemoryManager.cpp +++ b/src/LLVMMemoryManager.cpp @@ -4,7 +4,7 @@ * Implementation of the MM aware of LLVM specifics * such as function stack traversing. * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index ea21d39..8b436b3 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -4,7 +4,7 @@ * Implementation of MethodCompiler class which is used to * translate smalltalk bytecodes to LLVM IR code * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/NonCollectMemoryManager.cpp b/src/NonCollectMemoryManager.cpp index 9a9e389..fce5518 100644 --- a/src/NonCollectMemoryManager.cpp +++ b/src/NonCollectMemoryManager.cpp @@ -9,7 +9,7 @@ * production code. However, it may be helpful in various * test scenarios where small tasks are performed in one shot. * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/TDictionary.cpp b/src/TDictionary.cpp index 10cb539..25cf018 100644 --- a/src/TDictionary.cpp +++ b/src/TDictionary.cpp @@ -3,7 +3,7 @@ * * Implementation of TDictionary lookup methods * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/TSymbol.cpp b/src/TSymbol.cpp index ddfb06d..39905b4 100644 --- a/src/TSymbol.cpp +++ b/src/TSymbol.cpp @@ -3,7 +3,7 @@ * * Helper functions for TSymbol class * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/args.cpp b/src/args.cpp index a3c37e2..26452ff 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -3,7 +3,7 @@ * * Helper functions for command line argument parsing * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/llstDebuggingPass.cpp b/src/llstDebuggingPass.cpp index 7cab9f5..c20dba1 100644 --- a/src/llstDebuggingPass.cpp +++ b/src/llstDebuggingPass.cpp @@ -3,7 +3,7 @@ * * Debugging LLVM pass * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/llstPass.cpp b/src/llstPass.cpp index 7b9296c..03ade75 100644 --- a/src/llstPass.cpp +++ b/src/llstPass.cpp @@ -4,7 +4,7 @@ * Optimizing LLVM pass for JIT code generator. * Tries to remove redundant memory loads and roots. * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/main.cpp b/src/main.cpp index 2429f47..6474050 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ * * Program entry point * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/primitives.cpp b/src/primitives.cpp index c858373..df159a3 100644 --- a/src/primitives.cpp +++ b/src/primitives.cpp @@ -3,7 +3,7 @@ * * Implementation of primitive handling functions which are part of soft VM * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn diff --git a/src/vm.cpp b/src/vm.cpp index 05760ef..c09d474 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -3,7 +3,7 @@ * * Implementation of the virtual machine (SmalltalkVM class) * - * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.3 + * LLST (LLVM Smalltalk or Low Level Smalltalk) version 0.4 * * LLST is * Copyright (C) 2012-2015 by Dmitry Kashitsyn From 216a975a16f693fa368a11f2d60adea01ac8b107 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 9 Aug 2015 22:50:08 +0300 Subject: [PATCH 280/290] Bumps version of debian package --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64b601d..dc7e9c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,7 +164,7 @@ set (CPACK_DEBIAN_PACKAGE_DESCRIPTION # The format of Description: http://www.de ") set (CPACK_PACKAGE_CONTACT "info@llst.org") set (CPACK_PACKAGE_VERSION_MAJOR "0") -set (CPACK_PACKAGE_VERSION_MINOR "3") +set (CPACK_PACKAGE_VERSION_MINOR "4") set (CPACK_PACKAGE_VERSION_PATCH "0") set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set (CPACK_SOURCE_GENERATOR "TGZ") From 02e6018561070826aa75b8b97877eaaade670dec Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sun, 9 Aug 2015 23:39:09 +0300 Subject: [PATCH 281/290] Fixes primitive::stringAt --- src/primitives.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primitives.cpp b/src/primitives.cpp index c858373..6d7627b 100644 --- a/src/primitives.cpp +++ b/src/primitives.cpp @@ -87,7 +87,7 @@ TObject* callPrimitive(uint8_t opcode, TObjectArray* arguments, bool& primitiveF //valueObject is not used in primitive stringAtPut } - if (isSmallInteger(string) || string->getClass() != globals.stringClass || !isSmallInteger(indexObject)) { + if ( !isSmallInteger(indexObject) || isSmallInteger(string) ) { primitiveFailed = true; break; } From de82cfd2da56e443ee07f920c1a8fe034e006d17 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 11 Aug 2015 02:51:50 +0300 Subject: [PATCH 282/290] Prepares env and compilers --- .travis.yml | 7 ++++++- .travis/linux/before_install.sh | 3 +++ .travis/osx/before_install.sh | 11 +++++++++++ CMakeLists.txt | 8 ++------ cmake/FindTINFO.cmake | 12 ------------ 5 files changed, 22 insertions(+), 19 deletions(-) create mode 100755 .travis/linux/before_install.sh create mode 100755 .travis/osx/before_install.sh delete mode 100644 cmake/FindTINFO.cmake diff --git a/.travis.yml b/.travis.yml index 9a381cf..d3acf67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ compiler: os: - linux + - osx sudo: false cache: @@ -29,6 +30,10 @@ branches: - develop - /^.*travis.*$/ +matrix: + allow_failures: + - os: osx + env: matrix: # fix all scripts (e.g. install-dependencies.sh) if you rename LLVM_USE[On/Off] - USE_LLVM=Off @@ -43,7 +48,7 @@ env: BUILD_TYPE=Coverage before_install: - - pip install --user cpp-coveralls + - source .travis/${TRAVIS_OS_NAME}/before_install.sh before_script: - mkdir -p build diff --git a/.travis/linux/before_install.sh b/.travis/linux/before_install.sh new file mode 100755 index 0000000..951b301 --- /dev/null +++ b/.travis/linux/before_install.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +pip install --user cpp-coveralls diff --git a/.travis/osx/before_install.sh b/.travis/osx/before_install.sh new file mode 100755 index 0000000..00a62f5 --- /dev/null +++ b/.travis/osx/before_install.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [[ "${CC}" == "gcc" ]]; then + export CC=gcc-4.8 + export CXX=g++-4.8 +fi + +function brew_upgrade { brew outdated $1 || brew upgrade $1; } + +brew update +brew install readline diff --git a/CMakeLists.txt b/CMakeLists.txt index dc7e9c4..1239b72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,6 @@ project(llst) find_package(Threads REQUIRED QUIET) find_package(READLINE) -find_package(TINFO) find_package(LLVM ${LLVM_PACKAGE_VERSION} EXACT) find_package(POD2MAN) find_package(GZIP REQUIRED) @@ -34,14 +33,11 @@ else() endif() if (USE_READLINE) - if (READLINE_FOUND AND TINFO_FOUND) + if (READLINE_FOUND) message(STATUS "Using readline library") set_source_files_properties(src/CompletionEngine.cpp PROPERTIES COMPILE_DEFINITIONS USE_READLINE) - set (READLINE_LIBS_TO_LINK ${READLINE_LIBRARIES} ${TINFO_LIBRARIES}) + set (READLINE_LIBS_TO_LINK ${READLINE_LIBRARIES}) else() - if(NOT TINFO_FOUND) - message(SEND_ERROR "Library readline depends on tinfo.\nYou may configure with -DUSE_READLINE=OFF.") - endif() if(NOT READLINE_FOUND) message(SEND_ERROR "Library readline is not found.\nYou may configure with -DUSE_READLINE=OFF.") endif() diff --git a/cmake/FindTINFO.cmake b/cmake/FindTINFO.cmake deleted file mode 100644 index 5491120..0000000 --- a/cmake/FindTINFO.cmake +++ /dev/null @@ -1,12 +0,0 @@ - -# Find libtinfo (developer's library for the low-level terminfo library) -# Export variables: -# TINFO_FOUND -# TINFO_INCLUDE_DIRS -# TINFO_LIBRARIES - -find_library(TINFO_LIBRARIES NAMES tinfo) -mark_as_advanced(TINFO_LIBRARIES) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS( tinfo DEFAULT_MSG TINFO_LIBRARIES ) From 21cc732b1a45650e413827b73300d63b0bbbabaf Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 11 Aug 2015 23:42:54 +0300 Subject: [PATCH 283/290] Removes double compilation of some cpp files --- CMakeLists.txt | 11 ++--------- src/Image.cpp | 1 - tests/CMakeLists.txt | 10 +++++----- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1239b72..8f307eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,8 +84,6 @@ add_subdirectory(include) include_directories(include) add_library(stapi - src/TSmalltalkInstruction.cpp - src/InstructionDecoder.cpp src/ParsedBytecode.cpp src/ParsedMethod.cpp src/ParsedBlock.cpp @@ -107,22 +105,17 @@ add_library(memory_managers ${MM_CPP_FILES}) # Base set of sources needed in every build add_library(standart_set + src/vm.cpp src/args.cpp src/CompletionEngine.cpp src/Image.cpp src/primitives.cpp src/TDictionary.cpp src/TSymbol.cpp - src/vm.cpp src/TSmalltalkInstruction.cpp src/InstructionDecoder.cpp - src/ParsedMethod.cpp - src/ParsedBytecode.cpp - src/ParsedBlock.cpp - src/ControlGraph.cpp - src/ControlGraphVisualizer.cpp src/Timer.cpp src/GCLogger.cpp ) @@ -136,7 +129,7 @@ if (USE_LLVM) ) endif() -add_executable(llst src/main.cpp src/vm.cpp) +add_executable(llst src/main.cpp) add_dependencies(llst image) if (USE_LLVM) diff --git a/src/Image.cpp b/src/Image.cpp index c5d6f89..ed5df69 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -32,7 +32,6 @@ * along with LLST. If not, see . */ -#include #include //#include //TODO endianness diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b242a2c..a9a3a9a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,11 +14,11 @@ include_directories(${CMAKE_SOURCE_DIR}/include ${GTEST_INCLUDE_DIR}) add_subdirectory(radix_tree) -cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi") -cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi") -cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi") -cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi") -cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi") +cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;standart_set") +cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;standart_set") +cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;standart_set") +cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;standart_set") +cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;standart_set") # TODO cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi") cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set") cxx_test("VM::primitives" test_vm_primitives "${CMAKE_CURRENT_SOURCE_DIR}/vm_primitives.cpp" "memory_managers;standart_set") From 9745368db842887c009251b041155642317f2a55 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 11 Aug 2015 23:50:15 +0300 Subject: [PATCH 284/290] Fixes compilation of Timer.cpp not on Linux --- src/Timer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Timer.cpp b/src/Timer.cpp index ec5ed84..485f117 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -40,7 +40,7 @@ void Timer::start() { timeCreate = clock(); } -double Timer::getDiffSec() { +double Timer::getDiffSec() const { return static_cast((clock() - timeCreate))/CLOCKS_PER_SEC; } #endif From 142053785361d887fbf436219a5a7aad5110131a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 12 Aug 2015 00:11:46 +0300 Subject: [PATCH 285/290] Replaces get_current_dir_name with getcwd --- src/ControlGraphVisualizer.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/ControlGraphVisualizer.cpp b/src/ControlGraphVisualizer.cpp index 984bd27..6af5702 100644 --- a/src/ControlGraphVisualizer.cpp +++ b/src/ControlGraphVisualizer.cpp @@ -1,8 +1,37 @@ #include #include +#include +#include #include +void * gnu_xmalloc(size_t size) +{ + void* value = malloc(size); + if (value == 0) { + perror("virtual memory exhausted"); + abort(); + } + return value; +} + +std::string gnu_getcwd() { + size_t size = 100; + + while (true) { + char *buffer = reinterpret_cast( gnu_xmalloc(size) ); + if ( getcwd(buffer, size) == buffer ) { + std::string result(buffer); + free(buffer); + return result; + } + free(buffer); + if (errno != ERANGE) + return ""; + size *= 2; + } +} + std::string escape_path(const std::string& path) { if (path.empty()) return ""; @@ -30,7 +59,7 @@ ControlGraphVisualizer::ControlGraphVisualizer(st::ControlGraph* graph, const st m_stream.open(fullpath.c_str(), std::ios::out | std::ios::trunc); if (m_stream.fail()) { std::stringstream ss; - ss << "Cannot open/truncate '" << fullpath << "' in '" << get_current_dir_name() << "'"; + ss << "Cannot open/truncate '" << fullpath << "' in '" << gnu_getcwd() << "'"; throw std::ios_base::failure( ss.str() ); } m_stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); From 0ac76b295076de0476aad66a43fce3b5430dc9bf Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 12 Aug 2015 13:23:51 +0300 Subject: [PATCH 286/290] Moves some methods from instructions.h into stapi.h --- include/analysis.h | 2 +- include/instructions.h | 296 -------------------------------------- include/stapi.h | 315 +++++++++++++++++++++++++++++++++++++++++ src/ParsedBlock.cpp | 2 +- src/ParsedBytecode.cpp | 2 +- src/ParsedMethod.cpp | 2 +- 6 files changed, 319 insertions(+), 300 deletions(-) create mode 100644 include/stapi.h diff --git a/include/analysis.h b/include/analysis.h index 9d3f436..641f27e 100644 --- a/include/analysis.h +++ b/include/analysis.h @@ -4,7 +4,7 @@ #include #include -#include +#include namespace llvm { class Value; diff --git a/include/instructions.h b/include/instructions.h index bf4be31..4430c45 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -11,10 +11,6 @@ #include #include -namespace llvm { - class BasicBlock; -} - namespace st { struct TSmalltalkInstruction { @@ -89,298 +85,6 @@ class InstructionDecoder { uint16_t m_bytePointer; }; -class BasicBlock { -public: - typedef std::vector TInstructionVector; - typedef std::set TBasicBlockSet; - - class iterator : public TInstructionVector::iterator { - typedef TInstructionVector::iterator parent; - public: - iterator(const parent& copy) : parent(copy) { } - - const TSmalltalkInstruction operator *() const { - return TSmalltalkInstruction(parent::operator*()); - } - - parent& get() { return static_cast(*this); } - const parent& get() const { return static_cast(*this); } - }; - - iterator begin() { return iterator(m_instructions.begin()); } - iterator end() { return iterator(m_instructions.end()); } - - std::size_t size() const { return m_instructions.size(); } - - const TSmalltalkInstruction operator[](const std::size_t index) const { - return TSmalltalkInstruction(m_instructions[index]); - } - - // Append instruction to the end of basic block - void append(TSmalltalkInstruction instruction) { - m_instructions.push_back(instruction.serialize()); - } - - // Insert instruction at specified position - void insert(const iterator& position, TSmalltalkInstruction instruction) { - m_instructions.insert(position, instruction.serialize()); - } - - // Replace existing instruction at specified position with the new one - void replace(const iterator& position, TSmalltalkInstruction instruction) { - assert(position != m_instructions.end()); - - const TInstructionVector::iterator replacePosition = position; - *replacePosition = instruction.serialize(); - } - - // Remove instruction from basic block - void remove(const iterator& position) { - assert(position != m_instructions.end()); - m_instructions.erase(position); - } - - // Split current basic block at specified position - // Current block will hold instructions prior to the cut position - // Returned block will hold the rest - BasicBlock* split(const iterator& position) { - BasicBlock* const newBlock = new BasicBlock; - std::copy(position.get(), m_instructions.end(), newBlock->m_instructions.begin()); - m_instructions.erase(position, m_instructions.end()); - // TODO insert jump instruction and add newBlock to the parsed method - return newBlock; - } - - // Offset of first instruction of this basic block within the method's bytecodes - uint16_t getOffset() const { return m_offset; } - - // Set of basic blocks that are referencing current block by [conditionally] jumping into it - TBasicBlockSet& getReferers() { return m_referers; } - - bool getTerminator(TSmalltalkInstruction& out) const { - if (m_instructions.empty()) - return false; - - TSmalltalkInstruction result(m_instructions.back()); - if (result.isTerminator()) { - out = result; - return true; - } - - return false; - } - - void setValue(llvm::BasicBlock* value) { m_value = value; } - llvm::BasicBlock* getValue() const { return m_value; } - - void setEndValue(llvm::BasicBlock* value) { m_endValue = value; } - llvm::BasicBlock* getEndValue() const { return m_endValue; } - - BasicBlock(uint16_t blockOffset = 0) - : m_offset(blockOffset), m_value(0), m_endValue(0) { } - -private: - uint16_t m_offset; - TInstructionVector m_instructions; - TBasicBlockSet m_referers; - - llvm::BasicBlock* m_value; - llvm::BasicBlock* m_endValue; -}; - -// This is a base class for ParsedMethod and ParsedBlock -// Provides common interface for iterating basic blocks and -// instructions which is used by corresponding visitors -class ParsedBytecode { -public: - typedef std::list TBasicBlockList; - typedef TBasicBlockList::iterator iterator; - iterator begin() { return m_basicBlocks.begin(); } - iterator end() { return m_basicBlocks.end(); } - - BasicBlock* createBasicBlock(uint16_t blockOffset); - - virtual ~ParsedBytecode() { - for (TBasicBlockList::iterator iBlock = m_basicBlocks.begin(), - end = m_basicBlocks.end(); iBlock != end; ++iBlock) - { - delete * iBlock; - } - } - - // Returns actual method object which was parsed - TMethod* getOrigin() const { return m_origin; } - - BasicBlock* getBasicBlockByOffset(uint16_t offset) { - TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(offset); - if (iBlock != m_offsetToBasicBlock.end()) - return iBlock->second; - else - return 0; - } - -protected: - ParsedBytecode(TMethod* method) : m_origin(method) { } - void parse(uint16_t startOffset = 0, uint16_t stopOffset = 0); - uint16_t getNextBlockOffset(BasicBlock* currentBlock, uint16_t stopOffset); - void eraseReferer(uint16_t targetOffset, BasicBlock* referer); - void eraseBasicBlock(iterator& iBlock); - - // Descendants should override this method to provide block handling - virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset) = 0; - -protected: - TMethod* const m_origin; - TBasicBlockList m_basicBlocks; - - typedef std::map TOffsetToBasicBlockMap; - TOffsetToBasicBlockMap m_offsetToBasicBlock; - -private: - void updateReferences(BasicBlock* currentBasicBlock, BasicBlock* nextBlock, InstructionDecoder& decoder); -}; - -class ParsedBlock; -class ParsedMethod : public ParsedBytecode { - friend class ParsedBlock; // for addParsedBlock() - -public: - typedef std::list TParsedBlockList; - - ParsedMethod(TMethod* method) : ParsedBytecode(method) { - assert(method); - parse(); - } - - virtual ~ParsedMethod(); - - typedef TParsedBlockList::iterator block_iterator; - block_iterator blockBegin() { return m_parsedBlocks.begin(); } - block_iterator blockEnd() { return m_parsedBlocks.end(); } - - ParsedBlock* getParsedBlockByOffset(uint16_t startOffset) { - TOffsetToParsedBlockMap::iterator iBlock = m_offsetToParsedBlock.find(startOffset); - if (iBlock != m_offsetToParsedBlock.end()) - return iBlock->second; - else - return 0; - } - - ParsedBlock* getParsedBlockByEndOffset(uint16_t endOffset) { - TOffsetToParsedBlockMap::iterator iBlock = m_endOffsetToParsedBlock.find(endOffset); - if (iBlock != m_endOffsetToParsedBlock.end()) - return iBlock->second; - else - return 0; - } - -protected: - virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); - void addParsedBlock(ParsedBlock* parsedBlock); - -protected: - TParsedBlockList m_parsedBlocks; - - typedef std::map TOffsetToParsedBlockMap; - TOffsetToParsedBlockMap m_offsetToParsedBlock; - TOffsetToParsedBlockMap m_endOffsetToParsedBlock; -}; - -class ParsedBlock : public ParsedBytecode { -public: - ParsedBlock(ParsedMethod* parsedMethod, uint16_t startOffset, uint16_t stopOffset) - : ParsedBytecode(parsedMethod->getOrigin()), m_containerMethod(parsedMethod), - m_startOffset(startOffset), m_stopOffset(stopOffset) - { - parse(startOffset, stopOffset); - } - - // Parsed method encapsulating the block's bytecodes - ParsedMethod* getContainer() const { return m_containerMethod; } - - // First instruction offset within method's bytecodes - uint16_t getStartOffset() const { return m_startOffset; } - - // Instruction offset after the last block's instruction - uint16_t getStopOffset() const { return m_stopOffset; } - -protected: - virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); - -protected: - ParsedMethod* const m_containerMethod; - uint16_t m_startOffset; - uint16_t m_stopOffset; -}; - -class BasicBlockVisitor { -public: - BasicBlockVisitor(ParsedBytecode* parsedBytecode) : m_parsedBytecode(parsedBytecode) { } - virtual ~BasicBlockVisitor() { } - - virtual bool visitBlock(BasicBlock& /*basicBlock*/) { return true; } - - void run() { - ParsedBytecode::iterator iBlock = m_parsedBytecode->begin(); - const ParsedBytecode::iterator iEnd = m_parsedBytecode->end(); - - while (iBlock != iEnd) { - if (! visitBlock(** iBlock)) - break; - - ++iBlock; - } - } - -protected: - ParsedBytecode* const m_parsedBytecode; -}; - -class InstructionVisitor : public BasicBlockVisitor { -public: - InstructionVisitor(ParsedBytecode* parsedBytecode) : BasicBlockVisitor(parsedBytecode) { } - virtual bool visitInstruction(const TSmalltalkInstruction& /*instruction*/) { return true; } - -protected: - virtual bool visitBlock(BasicBlock& basicBlock) { - BasicBlock::iterator iInstruction = basicBlock.begin(); - const BasicBlock::iterator iEnd = basicBlock.end(); - - while (iInstruction != iEnd) { - if (! visitInstruction(* iInstruction)) - return false; - - ++iInstruction; - } - - return true; - } -}; - -class ParsedBlockVisitor { -public: - ParsedBlockVisitor(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } - virtual ~ParsedBlockVisitor() { } - - void run() { - ParsedMethod::block_iterator iBlock = m_parsedMethod->blockBegin(); - const ParsedMethod::block_iterator iEnd = m_parsedMethod->blockEnd(); - - while (iBlock != iEnd) { - if (! visitBlock(** iBlock)) - break; - - ++iBlock; - } - } - -protected: - virtual bool visitBlock(ParsedBlock& /*parsedBlock*/) { return true; } - -private: - ParsedMethod* const m_parsedMethod; -}; - } // namespace st #endif diff --git a/include/stapi.h b/include/stapi.h new file mode 100644 index 0000000..607c9d7 --- /dev/null +++ b/include/stapi.h @@ -0,0 +1,315 @@ +#ifndef LLST_STAPI_INCLUDED +#define LLST_STAPI_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace llvm { + class BasicBlock; +} + +namespace st { + +class BasicBlock { +public: + typedef std::vector TInstructionVector; + typedef std::set TBasicBlockSet; + + class iterator : public TInstructionVector::iterator { + typedef TInstructionVector::iterator parent; + public: + iterator(const parent& copy) : parent(copy) { } + + const TSmalltalkInstruction operator *() const { + return TSmalltalkInstruction(parent::operator*()); + } + + parent& get() { return static_cast(*this); } + const parent& get() const { return static_cast(*this); } + }; + + iterator begin() { return iterator(m_instructions.begin()); } + iterator end() { return iterator(m_instructions.end()); } + + std::size_t size() const { return m_instructions.size(); } + + const TSmalltalkInstruction operator[](const std::size_t index) const { + return TSmalltalkInstruction(m_instructions[index]); + } + + // Append instruction to the end of basic block + void append(TSmalltalkInstruction instruction) { + m_instructions.push_back(instruction.serialize()); + } + + // Insert instruction at specified position + void insert(const iterator& position, TSmalltalkInstruction instruction) { + m_instructions.insert(position, instruction.serialize()); + } + + // Replace existing instruction at specified position with the new one + void replace(const iterator& position, TSmalltalkInstruction instruction) { + assert(position != m_instructions.end()); + + const TInstructionVector::iterator replacePosition = position; + *replacePosition = instruction.serialize(); + } + + // Remove instruction from basic block + void remove(const iterator& position) { + assert(position != m_instructions.end()); + m_instructions.erase(position); + } + + // Split current basic block at specified position + // Current block will hold instructions prior to the cut position + // Returned block will hold the rest + BasicBlock* split(const iterator& position) { + BasicBlock* const newBlock = new BasicBlock; + std::copy(position.get(), m_instructions.end(), newBlock->m_instructions.begin()); + m_instructions.erase(position, m_instructions.end()); + // TODO insert jump instruction and add newBlock to the parsed method + return newBlock; + } + + // Offset of first instruction of this basic block within the method's bytecodes + uint16_t getOffset() const { return m_offset; } + + // Set of basic blocks that are referencing current block by [conditionally] jumping into it + TBasicBlockSet& getReferers() { return m_referers; } + + bool getTerminator(TSmalltalkInstruction& out) const { + if (m_instructions.empty()) + return false; + + TSmalltalkInstruction result(m_instructions.back()); + if (result.isTerminator()) { + out = result; + return true; + } + + return false; + } + + void setValue(llvm::BasicBlock* value) { m_value = value; } + llvm::BasicBlock* getValue() const { return m_value; } + + void setEndValue(llvm::BasicBlock* value) { m_endValue = value; } + llvm::BasicBlock* getEndValue() const { return m_endValue; } + + BasicBlock(uint16_t blockOffset = 0) + : m_offset(blockOffset), m_value(0), m_endValue(0) { } + +private: + uint16_t m_offset; + TInstructionVector m_instructions; + TBasicBlockSet m_referers; + + llvm::BasicBlock* m_value; + llvm::BasicBlock* m_endValue; +}; + +// This is a base class for ParsedMethod and ParsedBlock +// Provides common interface for iterating basic blocks and +// instructions which is used by corresponding visitors +class ParsedBytecode { +public: + typedef std::list TBasicBlockList; + typedef TBasicBlockList::iterator iterator; + iterator begin() { return m_basicBlocks.begin(); } + iterator end() { return m_basicBlocks.end(); } + + BasicBlock* createBasicBlock(uint16_t blockOffset); + + virtual ~ParsedBytecode() { + for (TBasicBlockList::iterator iBlock = m_basicBlocks.begin(), + end = m_basicBlocks.end(); iBlock != end; ++iBlock) + { + delete * iBlock; + } + } + + // Returns actual method object which was parsed + TMethod* getOrigin() const { return m_origin; } + + BasicBlock* getBasicBlockByOffset(uint16_t offset) { + TOffsetToBasicBlockMap::iterator iBlock = m_offsetToBasicBlock.find(offset); + if (iBlock != m_offsetToBasicBlock.end()) + return iBlock->second; + else + return 0; + } + +protected: + ParsedBytecode(TMethod* method) : m_origin(method) { } + void parse(uint16_t startOffset = 0, uint16_t stopOffset = 0); + uint16_t getNextBlockOffset(BasicBlock* currentBlock, uint16_t stopOffset); + void eraseReferer(uint16_t targetOffset, BasicBlock* referer); + void eraseBasicBlock(iterator& iBlock); + + // Descendants should override this method to provide block handling + virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset) = 0; + +protected: + TMethod* const m_origin; + TBasicBlockList m_basicBlocks; + + typedef std::map TOffsetToBasicBlockMap; + TOffsetToBasicBlockMap m_offsetToBasicBlock; + +private: + void updateReferences(BasicBlock* currentBasicBlock, BasicBlock* nextBlock, InstructionDecoder& decoder); +}; + +class ParsedBlock; +class ParsedMethod : public ParsedBytecode { + friend class ParsedBlock; // for addParsedBlock() + +public: + typedef std::list TParsedBlockList; + + ParsedMethod(TMethod* method) : ParsedBytecode(method) { + assert(method); + parse(); + } + + virtual ~ParsedMethod(); + + typedef TParsedBlockList::iterator block_iterator; + block_iterator blockBegin() { return m_parsedBlocks.begin(); } + block_iterator blockEnd() { return m_parsedBlocks.end(); } + + ParsedBlock* getParsedBlockByOffset(uint16_t startOffset) { + TOffsetToParsedBlockMap::iterator iBlock = m_offsetToParsedBlock.find(startOffset); + if (iBlock != m_offsetToParsedBlock.end()) + return iBlock->second; + else + return 0; + } + + ParsedBlock* getParsedBlockByEndOffset(uint16_t endOffset) { + TOffsetToParsedBlockMap::iterator iBlock = m_endOffsetToParsedBlock.find(endOffset); + if (iBlock != m_endOffsetToParsedBlock.end()) + return iBlock->second; + else + return 0; + } + +protected: + virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); + void addParsedBlock(ParsedBlock* parsedBlock); + +protected: + TParsedBlockList m_parsedBlocks; + + typedef std::map TOffsetToParsedBlockMap; + TOffsetToParsedBlockMap m_offsetToParsedBlock; + TOffsetToParsedBlockMap m_endOffsetToParsedBlock; +}; + +class ParsedBlock : public ParsedBytecode { +public: + ParsedBlock(ParsedMethod* parsedMethod, uint16_t startOffset, uint16_t stopOffset) + : ParsedBytecode(parsedMethod->getOrigin()), m_containerMethod(parsedMethod), + m_startOffset(startOffset), m_stopOffset(stopOffset) + { + parse(startOffset, stopOffset); + } + + // Parsed method encapsulating the block's bytecodes + ParsedMethod* getContainer() const { return m_containerMethod; } + + // First instruction offset within method's bytecodes + uint16_t getStartOffset() const { return m_startOffset; } + + // Instruction offset after the last block's instruction + uint16_t getStopOffset() const { return m_stopOffset; } + +protected: + virtual void parseBlock(uint16_t startOffset, uint16_t stopOffset); + +protected: + ParsedMethod* const m_containerMethod; + uint16_t m_startOffset; + uint16_t m_stopOffset; +}; + +class BasicBlockVisitor { +public: + BasicBlockVisitor(ParsedBytecode* parsedBytecode) : m_parsedBytecode(parsedBytecode) { } + virtual ~BasicBlockVisitor() { } + + virtual bool visitBlock(BasicBlock& /*basicBlock*/) { return true; } + + void run() { + ParsedBytecode::iterator iBlock = m_parsedBytecode->begin(); + const ParsedBytecode::iterator iEnd = m_parsedBytecode->end(); + + while (iBlock != iEnd) { + if (! visitBlock(** iBlock)) + break; + + ++iBlock; + } + } + +protected: + ParsedBytecode* const m_parsedBytecode; +}; + +class InstructionVisitor : public BasicBlockVisitor { +public: + InstructionVisitor(ParsedBytecode* parsedBytecode) : BasicBlockVisitor(parsedBytecode) { } + virtual bool visitInstruction(const TSmalltalkInstruction& /*instruction*/) { return true; } + +protected: + virtual bool visitBlock(BasicBlock& basicBlock) { + BasicBlock::iterator iInstruction = basicBlock.begin(); + const BasicBlock::iterator iEnd = basicBlock.end(); + + while (iInstruction != iEnd) { + if (! visitInstruction(* iInstruction)) + return false; + + ++iInstruction; + } + + return true; + } +}; + +class ParsedBlockVisitor { +public: + ParsedBlockVisitor(ParsedMethod* parsedMethod) : m_parsedMethod(parsedMethod) { } + virtual ~ParsedBlockVisitor() { } + + void run() { + ParsedMethod::block_iterator iBlock = m_parsedMethod->blockBegin(); + const ParsedMethod::block_iterator iEnd = m_parsedMethod->blockEnd(); + + while (iBlock != iEnd) { + if (! visitBlock(** iBlock)) + break; + + ++iBlock; + } + } + +protected: + virtual bool visitBlock(ParsedBlock& /*parsedBlock*/) { return true; } + +private: + ParsedMethod* const m_parsedMethod; +}; + +} // namespace st + +#endif diff --git a/src/ParsedBlock.cpp b/src/ParsedBlock.cpp index edc92fc..a1e343d 100644 --- a/src/ParsedBlock.cpp +++ b/src/ParsedBlock.cpp @@ -1,4 +1,4 @@ -#include +#include using namespace st; diff --git a/src/ParsedBytecode.cpp b/src/ParsedBytecode.cpp index 3e1a146..c778e85 100644 --- a/src/ParsedBytecode.cpp +++ b/src/ParsedBytecode.cpp @@ -1,5 +1,5 @@ #include -#include +#include using namespace st; diff --git a/src/ParsedMethod.cpp b/src/ParsedMethod.cpp index 562bb8b..689609e 100644 --- a/src/ParsedMethod.cpp +++ b/src/ParsedMethod.cpp @@ -1,4 +1,4 @@ -#include +#include using namespace st; From 35a981a7039a5ea24cbd5746ed6f753d9c450f87 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 12 Aug 2015 15:32:26 +0300 Subject: [PATCH 287/290] Bumps version in `--version` --- src/args.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/args.cpp b/src/args.cpp index 26452ff..70b03bb 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -130,7 +130,7 @@ std::string args::getHelp() std::string args::getVersion() { return - "llst 0.3.0\n" + "llst 0.4.0\n" "Copyright (C) 2012-2015 by Dmitry Kashitsyn \n" "Copyright (C) 2012-2015 by Roman Proskuryakov \n" "This is free software; see the source for copying conditions. There is NO\n" From abb700bf4926a452fd08f57c1b64f4a19f43ad20 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 12 Aug 2015 23:14:40 +0300 Subject: [PATCH 288/290] Installs llvm 3.3 under osx --- .travis/osx/before_install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis/osx/before_install.sh b/.travis/osx/before_install.sh index 00a62f5..66e7743 100755 --- a/.travis/osx/before_install.sh +++ b/.travis/osx/before_install.sh @@ -9,3 +9,4 @@ function brew_upgrade { brew outdated $1 || brew upgrade $1; } brew update brew install readline +brew install llvm33 From 47cb589e4f7777c455a7a7bc880774ca76087e95 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 12 Aug 2015 23:36:17 +0300 Subject: [PATCH 289/290] Fixes compilation of MethodCompiler::getSkipOffset --- src/MethodCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index 8b436b3..d4e8860 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -1396,7 +1396,7 @@ void MethodCompiler::doSpecial(TJITContext& jit) uint16_t MethodCompiler::getSkipOffset(st::InstructionNode* branch) { assert(branch->getInstruction().isBranch()); - assert(branch->getInstruction().opcode != special::branch); + assert(branch->getInstruction().getOpcode() != special::branch); assert(branch->getOutEdges().size() == 2); // One of the offsets we know. It is the target offset when condition is met. From 78cbdd04c0d4fc5517e24695c5b373e0597b1efe Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Thu, 13 Aug 2015 15:10:52 +0300 Subject: [PATCH 290/290] Renames standart_set -> standard_set --- CMakeLists.txt | 4 ++-- tests/CMakeLists.txt | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f307eb..ec82be7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,7 @@ endif() add_library(memory_managers ${MM_CPP_FILES}) # Base set of sources needed in every build -add_library(standart_set +add_library(standard_set src/vm.cpp src/args.cpp src/CompletionEngine.cpp @@ -135,7 +135,7 @@ add_dependencies(llst image) if (USE_LLVM) target_link_libraries(llst jit ${LLVM_LIBS} ${LLVM_LD_FLAGS}) endif() -target_link_libraries(llst standart_set memory_managers stapi ${READLINE_LIBS_TO_LINK} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) +target_link_libraries(llst standard_set memory_managers stapi ${READLINE_LIBS_TO_LINK} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) set(changelog_compressed "${CMAKE_CURRENT_BINARY_DIR}/changelog.gz") gzip_compress("compress_changelog" "${CMAKE_CURRENT_SOURCE_DIR}/ChangeLog" ${changelog_compressed}) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a9a3a9a..98a7cde 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,11 +14,11 @@ include_directories(${CMAKE_SOURCE_DIR}/include ${GTEST_INCLUDE_DIR}) add_subdirectory(radix_tree) -cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;standart_set") -cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;standart_set") -cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;standart_set") -cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;standart_set") -cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;standart_set") +cxx_test(TSmalltalkInstruction test_tsmalltalk_instruction "${CMAKE_CURRENT_SOURCE_DIR}/tsmalltalk_instruction.cpp" "stapi;standard_set") +cxx_test(InstructionDecoder test_instruction_decoder "${CMAKE_CURRENT_SOURCE_DIR}/instruction_decoder.cpp" "stapi;standard_set") +cxx_test(ControlGraph test_control_graph "${CMAKE_CURRENT_SOURCE_DIR}/control_graph.cpp" "stapi;standard_set") +cxx_test(ABABProblem test_abab_problem "${CMAKE_CURRENT_SOURCE_DIR}/abab_problem.cpp" "stapi;standard_set") +cxx_test(StackSemantics test_stack_semantics "${CMAKE_CURRENT_SOURCE_DIR}/stack_semantics.cpp" "stapi;standard_set") # TODO cxx_test(StackUnderflow test_stack_underflow "${CMAKE_CURRENT_SOURCE_DIR}/stack_underflow.cpp" "stapi") -cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standart_set") -cxx_test("VM::primitives" test_vm_primitives "${CMAKE_CURRENT_SOURCE_DIR}/vm_primitives.cpp" "memory_managers;standart_set") +cxx_test(DecodeAllMethods test_decode_all_methods "${CMAKE_CURRENT_SOURCE_DIR}/decode_all_methods.cpp" "stapi;memory_managers;standard_set") +cxx_test("VM::primitives" test_vm_primitives "${CMAKE_CURRENT_SOURCE_DIR}/vm_primitives.cpp" "memory_managers;standard_set")