Skip to content

Commit

Permalink
Adds basic logic of type analyzer and inference API
Browse files Browse the repository at this point in the history
Issue: #17
  • Loading branch information
0x7CFE committed May 21, 2016
1 parent 0e3b329 commit 1789190
Show file tree
Hide file tree
Showing 3 changed files with 364 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ add_library(stapi

src/ControlGraph.cpp
src/ControlGraphVisualizer.cpp
src/TypeAnalyzer.cpp
)

set(MM_CPP_FILES
Expand Down
176 changes: 176 additions & 0 deletions include/inference.h
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
187 changes: 187 additions & 0 deletions src/TypeAnalyzer.cpp
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() {

}

0 comments on commit 1789190

Please sign in to comment.