-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds basic logic of type analyzer and inference API
Issue: #17
- Loading branch information
Showing
3 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
#ifndef LLST_INFERENCE_H_INCLUDED | ||
#define LLST_INFERENCE_H_INCLUDED | ||
|
||
#include <vm.h> | ||
#include <analysis.h> | ||
|
||
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<Type> 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<Type> 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<const InstructionNode&>(node)); | ||
break; | ||
|
||
case ControlNode::ntPhi: | ||
analyzer.processPhi(static_cast<const PhiNode&>(node)); | ||
break; | ||
|
||
case ControlNode::ntTau: | ||
analyzer.processTau(static_cast<const TauNode&>(node)); | ||
break; | ||
} | ||
|
||
return vrKeepWalking; | ||
} | ||
|
||
void nodesVisited() { | ||
analyzer.walkComplete(); | ||
} | ||
|
||
private: | ||
TypeAnalyzer& analyzer; | ||
}; | ||
|
||
}; | ||
|
||
} // namespace type | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
#include <inference.h> | ||
|
||
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<unaryBuiltIns::Opcode>(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<binaryBuiltIns::Operator>(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<InstructionNode>()]); | ||
|
||
result.setKind(Type::tkComposite); | ||
} | ||
|
||
void TypeAnalyzer::processTau(const TauNode& tau) { | ||
Type& result = m_context[tau]; | ||
result.setKind(Type::tkPolytype); | ||
} | ||
|
||
void TypeAnalyzer::walkComplete() { | ||
|
||
} |