From 1789190ccf3f3e0dfc41c32edc7dc05c16b0d77f Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sun, 15 May 2016 19:28:32 +0600 Subject: [PATCH] Adds basic logic of type analyzer and inference API Issue: #17 --- CMakeLists.txt | 1 + include/inference.h | 176 ++++++++++++++++++++++++++++++++++++++++ src/TypeAnalyzer.cpp | 187 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 include/inference.h create mode 100644 src/TypeAnalyzer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 98dfac4..0d4679d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ add_library(stapi src/ControlGraph.cpp src/ControlGraphVisualizer.cpp + src/TypeAnalyzer.cpp ) set(MM_CPP_FILES diff --git a/include/inference.h b/include/inference.h new file mode 100644 index 0000000..3b9fdbf --- /dev/null +++ b/include/inference.h @@ -0,0 +1,176 @@ +#ifndef LLST_INFERENCE_H_INCLUDED +#define LLST_INFERENCE_H_INCLUDED + +#include +#include + +namespace type { + +using namespace st; + +class Type { +public: + enum TKind { + tkUndefined = 0, + tkLiteral, + tkMonotype, + tkComposite, + tkArray, + tkPolytype + }; + + // Return a string representation of a type: + // Kind Representation Example + // tkUndefined ? ? + // tkPolytype * * + // tkLiteral literal value 42 + // tkMonotype (class name) (SmallInt) + // tkComposite (class name, ...) (SmallInt, *) + // tkArray class name [...] Array[String, *, (*, *), (True, False)] + std::string toString() const; + + Type(TKind kind = tkUndefined) : m_kind(kind), m_value(0) {} + Type(TObject* literal) { set(literal); } + Type(TClass* klass) { set(klass); } + + void setKind(TKind kind) { m_kind = kind; } + TKind getKind() const { return m_kind; } + TObject* getValue() const { return m_value; } + + void reset() { + m_kind = tkUndefined; + m_value = 0; + m_subTypes.clear(); + } + + void set(TObject* literal, TKind kind = tkLiteral) { + m_kind = kind; + m_value = literal; + } + + void set(TClass* klass, TKind kind = tkMonotype) { + m_kind = kind; + m_value = klass; + } + + typedef std::vector TSubTypes; + const TSubTypes& getSubTypes() const { return m_subTypes; } + const Type& operator[] (std::size_t index) const { return m_subTypes[index]; } + + void addSubType(const Type& type) { m_subTypes.push_back(type); } + +private: + TKind m_kind; + TObject* m_value; + TSubTypes m_subTypes; +}; + +typedef std::vector TTypeList; + +class CallContext { +public: + CallContext(std::size_t index, const Type& arguments, std::size_t nodeCount) + : m_index(index), m_arguments(arguments) + { + m_instructions.resize(nodeCount); + } + + std::size_t getIndex() const { return m_index; } + + const Type& getArgument(std::size_t index) const { + static const Type polytype(Type::tkPolytype); + + if (m_arguments.getKind() != Type::tkPolytype) + return m_arguments[index]; + else + return polytype; + } + const Type& getArguments() const { return m_arguments; } + + Type& getReturnType() { return m_returnType; } + + Type& getInstructionType(std::size_t index) { return m_instructions[index]; } + Type& operator[] (const ControlNode& node) { return getInstructionType(node.getIndex()); } + + +private: + const std::size_t m_index; + const Type m_arguments; + TTypeList m_instructions; + Type m_returnType; +}; + +class TypeSystem { +public: + CallContext* newCallContext(TMethod* method, const Type& arguments = Type(Type::tkPolytype)); + + CallContext* getCallContext(std::size_t index); +}; + +class TypeAnalyzer { +public: + TypeAnalyzer(ControlGraph& graph, CallContext& context) + : m_graph(graph), m_context(context) {} + + void run() { + if (m_graph.isEmpty()) + return; + + Walker walker(*this); + walker.run(*m_graph.nodes_begin(), Walker::wdForward); + } + +private: + ControlGraph& m_graph; + CallContext& m_context; + +private: + void processInstruction(const InstructionNode& instruction); + void processPhi(const PhiNode& phi); + void processTau(const TauNode& tau); + void walkComplete(); + + void doMarkArguments(const InstructionNode& instruction); + void doPushConstant(const InstructionNode& instruction); + void doPushLiteral(const InstructionNode& instruction); + void doSendUnary(const InstructionNode& instruction); + void doSendBinary(const InstructionNode& instruction); + +private: + + class Walker : public GraphWalker { + public: + Walker(TypeAnalyzer& analyzer) : analyzer(analyzer) {} + + private: + TVisitResult visitNode(ControlNode& node, const TPathNode*) { + switch (node.getNodeType()) { + case ControlNode::ntInstruction: + analyzer.processInstruction(static_cast(node)); + break; + + case ControlNode::ntPhi: + analyzer.processPhi(static_cast(node)); + break; + + case ControlNode::ntTau: + analyzer.processTau(static_cast(node)); + break; + } + + return vrKeepWalking; + } + + void nodesVisited() { + analyzer.walkComplete(); + } + + private: + TypeAnalyzer& analyzer; + }; + +}; + +} // namespace type + +#endif diff --git a/src/TypeAnalyzer.cpp b/src/TypeAnalyzer.cpp new file mode 100644 index 0000000..81e6286 --- /dev/null +++ b/src/TypeAnalyzer.cpp @@ -0,0 +1,187 @@ +#include + +using namespace type; + +void TypeAnalyzer::processInstruction(const InstructionNode& instruction) { + const TSmalltalkInstruction::TArgument argument = instruction.getInstruction().getArgument(); + + switch (instruction.getInstruction().getOpcode()) { + case opcode::pushArgument: + m_context[instruction] = m_context.getArgument(argument); + break; + + case opcode::pushConstant: doPushConstant(instruction); break; + case opcode::pushLiteral: doPushLiteral(instruction); break; + case opcode::markArguments: doMarkArguments(instruction); break; + + case opcode::sendBinary: doSendBinary(instruction); break; + + case opcode::sendMessage: + // For now, treat method call as * + m_context[instruction] = Type(Type::tkPolytype); + break; + + default: + break; + } +} + +void TypeAnalyzer::doPushConstant(const InstructionNode& instruction) { + const TSmalltalkInstruction::TArgument argument = instruction.getInstruction().getArgument(); + Type& type = m_context[instruction]; + + switch (argument) { + case 0: case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: case 9: + type.set(TInteger(argument)); + break; + + case pushConstants::nil: type.set(globals.nilObject); break; + case pushConstants::trueObject: type.set(globals.trueObject); break; + case pushConstants::falseObject: type.set(globals.falseObject); break; + + default: + std::fprintf(stderr, "VM: unknown push constant %d\n", argument); + type.reset(); + } +} + +void TypeAnalyzer::doPushLiteral(const InstructionNode& instruction) { + TMethod* const method = m_graph.getParsedMethod()->getOrigin(); + const TSmalltalkInstruction::TArgument argument = instruction.getInstruction().getArgument(); + TObject* const literal = method->literals->getField(argument); + + m_context[instruction] = Type(literal); +} + +void TypeAnalyzer::doSendUnary(const InstructionNode& instruction) { + const Type& argType = m_context[*instruction.getArgument()]; + const unaryBuiltIns::Opcode opcode = static_cast(instruction.getInstruction().getArgument()); + + Type& result = m_context[instruction]; + switch (argType.getKind()) { + case Type::tkLiteral: + case Type::tkMonotype: + { + const bool isValueNil = + (argType.getValue() == globals.nilObject) + || (argType.getValue() == globals.nilObject->getClass()); + + if (opcode == unaryBuiltIns::isNil) + result.set(isValueNil ? globals.trueObject : globals.falseObject); + else + result.set(isValueNil ? globals.falseObject : globals.trueObject); + break; + } + + case Type::tkComposite: + case Type::tkArray: + { + // TODO Repeat the procedure over each subtype + result.setKind(Type::tkPolytype); + break; + } + + default: + // * isNil = (Boolean) + // * notNil = (Boolean) + result.set(globals.trueObject->getClass()->getClass()); + } + +} + +void TypeAnalyzer::doSendBinary(const InstructionNode& instruction) { + const Type& type1 = m_context[*instruction.getArgument(0)]; + const Type& type2 = m_context[*instruction.getArgument(1)]; + const binaryBuiltIns::Operator opcode = static_cast(instruction.getInstruction().getArgument()); + + Type& result = m_context[instruction]; + + if (isSmallInteger(type1.getValue()) && isSmallInteger(type1.getValue())) { + const int32_t rightOperand = TInteger(type2.getValue()); + const int32_t leftOperand = TInteger(type1.getValue()); + + switch (opcode) { + case binaryBuiltIns::operatorLess: + result.set((leftOperand < rightOperand) ? globals.trueObject : globals.falseObject); + break; + + case binaryBuiltIns::operatorLessOrEq: + result.set((leftOperand <= rightOperand) ? globals.trueObject : globals.falseObject); + break; + + case binaryBuiltIns::operatorPlus: + result.set(TInteger(leftOperand + rightOperand)); + break; + + default: + std::fprintf(stderr, "VM: Invalid opcode %d passed to sendBinary\n", opcode); + } + + return; + } + + // Literal int or (SmallInt) monotype + const bool isInt1 = isSmallInteger(type1.getValue()) || type1.getValue() == globals.smallIntClass; + const bool isInt2 = isSmallInteger(type2.getValue()) || type2.getValue() == globals.smallIntClass; + + if (isInt1 && isInt2) { + switch (opcode) { + case binaryBuiltIns::operatorLess: + case binaryBuiltIns::operatorLessOrEq: + // (SmallInt) <= (SmallInt) = (Boolean) + result.set(globals.trueObject->getClass()->getClass()); + break; + + case binaryBuiltIns::operatorPlus: + // (SmallInt) + (SmallInt) = (SmallInt) + result.set(globals.smallIntClass); + break; + + default: + std::fprintf(stderr, "VM: Invalid opcode %d passed to sendBinary\n", opcode); + result.reset(); // ? + } + + return; + } + + // TODO In case of complex invocation encode resursive analysis of operator as a message + + result.setKind(Type::tkPolytype); +} + +void TypeAnalyzer::doMarkArguments(const InstructionNode& instruction) { + const Type& argsType = m_context[*instruction.getArgument()]; + Type& result = m_context[instruction]; + + if (argsType.getKind() == Type::tkUndefined || argsType.getKind() == Type::tkPolytype) { + result.set(globals.arrayClass); + return; + } + + for (std::size_t index = 0; index < argsType.getSubTypes().size(); index++) + result.addSubType(argsType[index]); + + result.set(globals.arrayClass, Type::tkArray); +} + +void TypeAnalyzer::processPhi(const PhiNode& phi) { + Type& result = m_context[phi]; + + const TNodeSet& incomings = phi.getRealValues(); + TNodeSet::iterator iNode = incomings.begin(); + for (; iNode != incomings.end(); ++iNode) + result.addSubType(m_context[*(*iNode)->cast()]); + + result.setKind(Type::tkComposite); +} + +void TypeAnalyzer::processTau(const TauNode& tau) { + Type& result = m_context[tau]; + result.setKind(Type::tkPolytype); +} + +void TypeAnalyzer::walkComplete() { + +}