Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libsolidity/ast/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ u256 RationalNumberType::literalValue(Literal const*) const
value = u256(shiftedValue);
else
value = s2u(s256(shiftedValue));
cout << value << endl;
return value;
}

Expand Down
63 changes: 58 additions & 5 deletions libsolidity/codegen/CompilerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,6 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, "");
break;
case Type::Category::FixedPoint:
solAssert(false, "Not yet implemented - FixedPointType.");
case Type::Category::Integer:
case Type::Category::Contract:
case Type::Category::RationalNumber:
Expand Down Expand Up @@ -372,10 +371,36 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
);
//shift all integer bits onto the left side of the fixed type
FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType);
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
if (targetFixedPointType.integerBits() > typeOnStack->numBits())
cleanHigherOrderBits(*typeOnStack);
solAssert(false, "Not yet implemented - FixedPointType.");
if (auto intType = dynamic_cast<IntegerType const*>(&_typeOnStack))
{
if (targetFixedPointType.integerBits() > intType->numBits())
cleanHigherOrderBits(*intType);
u256 shiftFactor = u256(1) << (targetFixedPointType.fractionalBits());
m_context << shiftFactor << Instruction::MUL;
}
else if (auto rationalType = dynamic_cast<RationalNumberType const*>(&_typeOnStack))
{
if (
(
targetFixedPointType.integerBits() > rationalType->fixedPointType()->integerBits() ||
targetFixedPointType.fractionalBits() > rationalType->fixedPointType()->fractionalBits()
) && _cleanupNeeded
)
cleanHigherOrderBits(targetFixedPointType);
fixedPointShifting(*rationalType->fixedPointType(), targetFixedPointType);
}
else
{
FixedPointType const& stackFixedType = dynamic_cast<FixedPointType const&>(_typeOnStack);
if (
targetFixedPointType.integerBits() > stackFixedType.integerBits() ||
targetFixedPointType.fractionalBits() > stackFixedType.fractionalBits()
)
cleanHigherOrderBits(stackFixedType);
else if (_cleanupNeeded)
cleanHigherOrderBits(targetFixedPointType);
fixedPointShifting(stackFixedType, targetFixedPointType);
}
}
else
{
Expand Down Expand Up @@ -823,6 +848,34 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND;
}

void CompilerUtils::cleanHigherOrderBits(FixedPointType const& _typeOnStack)
{
if (_typeOnStack.numBits() == 256)
return;
else if (_typeOnStack.isSigned())
m_context << u256(_typeOnStack.numBits() / 8 - 1) << Instruction::SIGNEXTEND;
else
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND;
}

void CompilerUtils::fixedPointShifting(FixedPointType const& _stackType, FixedPointType const& _targetType)
{
u256 shiftFactor;
int targetFractionalBits = _targetType.fractionalBits();
int stackFractionalBits = _stackType.fractionalBits();

if (stackFractionalBits > targetFractionalBits)
{
shiftFactor = (u256(1) << (stackFractionalBits - targetFractionalBits));
m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
}
else if (stackFractionalBits < targetFractionalBits)
{
shiftFactor = (u256(1) << (targetFractionalBits - stackFractionalBits));
m_context << shiftFactor << Instruction::MUL;
}
}

unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
{
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
Expand Down
4 changes: 3 additions & 1 deletion libsolidity/codegen/CompilerUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ class CompilerUtils
/// Stack post:
void storeStringData(bytesConstRef _data);

/// Helper function for handling fixed point shifting
void fixedPointShifting(FixedPointType const& _stackType, FixedPointType const& _targetType);
/// Appends code that cleans higher-order bits for integer types.
void cleanHigherOrderBits(IntegerType const& _typeOnStack);

void cleanHigherOrderBits(FixedPointType const& _typeOnStack);
/// Prepares the given type for storing in memory by shifting it if necessary.
unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const;
/// Loads type from memory assuming memory offset is on stack top.
Expand Down
157 changes: 147 additions & 10 deletions libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
#include <utility>
#include <numeric>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/SHA3.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/ExpressionCompiler.h>
#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/LValue.h>
#include <libsolidity/inlineasm/AsmStack.h>
#include <libevmasm/GasMeter.h>
using namespace std;

Expand Down Expand Up @@ -307,6 +309,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
break;
case Token::Inc: // ++ (pre- or postfix)
case Token::Dec: // -- (pre- or postfix)
{
solAssert(!!m_currentLValue, "LValue not retrieved.");
m_currentLValue->retrieveValue(_unaryOperation.location());
if (!_unaryOperation.isPrefixOperation())
Expand All @@ -318,7 +321,16 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i);
}
m_context << u256(1);

u256 oneValue;
if (_unaryOperation.annotation().type->category() == Type::Category::FixedPoint)
{
FixedPointType const& fixedType = dynamic_cast<FixedPointType const&>(*_unaryOperation.subExpression().annotation().type);
oneValue = u256(1) << fixedType.fractionalBits();
}
else
oneValue = u256(1);
m_context << oneValue;
if (_unaryOperation.getOperator() == Token::Inc)
m_context << Instruction::ADD;
else
Expand All @@ -329,9 +341,11 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << swapInstruction(i);
m_currentLValue->storeValue(
*_unaryOperation.annotation().type, _unaryOperation.location(),
!_unaryOperation.isPrefixOperation());
!_unaryOperation.isPrefixOperation()
);
m_currentLValue.reset();
break;
}
case Token::Add: // +
// unary add, so basically no-op
break;
Expand Down Expand Up @@ -361,9 +375,14 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
else
{
bool cleanupNeeded = false;

if (Token::isCompareOp(c_op))
cleanupNeeded = true;
if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod))
if (
(commonType.category() == Type::Category::Integer &&
(c_op == Token::Div || c_op == Token::Mod)) ||
commonType.category() == Type::Category::FixedPoint
)
cleanupNeeded = true;

// for commutative operators, push the literal as late as possible to allow improved optimization
Expand Down Expand Up @@ -1231,7 +1250,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
case Type::Category::StringLiteral:
break; // will be done during conversion
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, rational, boolean and string literals implemented for now."));
}
}

Expand Down Expand Up @@ -1263,6 +1282,8 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
bool isSigned = false;
if (auto type = dynamic_cast<IntegerType const*>(&_type))
isSigned = type->isSigned();
else if (auto type = dynamic_cast<FixedPointType const*>(&_type))
isSigned = type->isSigned();

switch (_operator)
{
Expand Down Expand Up @@ -1302,11 +1323,34 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator

void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
bool const c_isSigned = type.isSigned();
bool c_isSigned;
bool c_isFractional;
u256 c_fractionShift = u256(1);
u256 c_halfShift = u256(1);
int c_fractionalBits = 0;
int c_numBits = 0;
int c_intBits = 0;

if (_type.category() == Type::Category::Integer)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
c_isSigned = type.isSigned();
c_isFractional = false;
}
else if (_type.category() == Type::Category::FixedPoint)
{
FixedPointType const& type = dynamic_cast<FixedPointType const&>(_type);
c_isSigned = type.isSigned();
c_isFractional = true;
c_fractionalBits = type.fractionalBits();
c_intBits = type.integerBits();
c_numBits = type.numBits();
c_fractionShift = (u256(1) << (c_fractionalBits));
c_halfShift = (u256(1) << (c_fractionalBits / 2));
cout << "Fractional bits: " << type.fractionalBits() << endl;
cout << "Integer bits: " << type.integerBits() << endl;
}

if (_type.category() == Type::Category::FixedPoint)
solAssert(false, "Not yet implemented - FixedPointType.");

switch (_operator)
{
Expand All @@ -1317,10 +1361,90 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
m_context << Instruction::SUB;
break;
case Token::Mul:
m_context << Instruction::MUL;
if (c_isFractional) //fixed point handling
{
if (c_numBits - c_fractionalBits == 0)
{
//m_context << Instruction::DUP1 << Instruction::DUP2;
//take two numbers, cut them in half, multiply them, simple as that.
//m_context << c_halfShift << Instruction::DUP1 << Instruction::SWAP2 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
//m_context << Instruction::SWAP2 << (c_isSigned ? Instruction::SDIV : Instruction::DIV) << Instruction::MUL;
m_context << Instruction::MUL;
}
else if (c_numBits > 128)
{
//duplicate converted types for reusage
appendInlineAssembly(R"(
let A := dup1
let B := dup3
let AHigh := $mod($fractionShift, A)
let BHigh := $mod($fractionShift, B)
)", map<string, string>{
{"$fractionShift", toString(c_fractionShift)},
{"$mod", (c_isSigned ? "mod" : "smod"}
});

//time to get schwifty in here...
//split both numbers into their representative fractional and decimal parts
//this takes the form A.B * C.D
//then we utilize this formula:
//D*B/shift + C*A*shift + D*A + C*B

//D*B/shift
m_context << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
m_context << Instruction::SWAP1 << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
if (c_fractionalBits > c_intBits)
{
//we need this here because overwise we will have an overflow with our fractional bits
m_context << c_halfShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
m_context << Instruction::SWAP1 << c_halfShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV) << Instruction::MUL;
}
else
m_context << Instruction::MUL << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
//C*A*shift*/
m_context << Instruction::DUP2 << Instruction::DUP4;
m_context << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
m_context << Instruction::SWAP1 << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
m_context << Instruction::MUL << c_fractionShift << Instruction::MUL << Instruction::ADD;
//D*A
m_context << Instruction::DUP2 << Instruction::DUP4;
m_context << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
m_context << Instruction::SWAP1 << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
m_context << Instruction::MUL << Instruction::ADD;
//C*B
m_context << Instruction::SWAP2 << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
m_context << Instruction::SWAP1 << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
m_context << Instruction::MUL << Instruction::ADD;
}
else
{
//multiply, then shift right...this is your fraction.
m_context << Instruction::MUL << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
//now redo the process...
m_context << Instruction::DUP2 << Instruction::DUP4;
//but this time, get the integer portion.
m_context << c_fractionShift << Instruction::SWAP1 << (c_isSigned ? Instruction::SDIV : Instruction::DIV) << Instruction::MUL;
//add
m_context << Instruction::ADD;
}
}
else
m_context << Instruction::MUL;
break;
case Token::Div:
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
if (c_isFractional)
{
//divide, then shift left...this is your integer.
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV) << c_fractionShift << Instruction::MUL;
//now redo the process...
m_context << Instruction::DUP2 << Instruction::DUP4;
//but this time, get the fraction portion.
m_context << c_fractionShift << Instruction::SWAP1 << Instruction::MUL << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
//add
m_context << Instruction::ADD;
}
else
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
break;
case Token::Mod:
m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
Expand Down Expand Up @@ -1586,5 +1710,18 @@ CompilerUtils ExpressionCompiler::utils()
return CompilerUtils(m_context);
}

void ExpressionCompiler::appendInlineAssembly(string const& _assembly, map<string, string> const& _replacements)
{
if (_replacements.empty())
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(_assembly, m_context.nonConstAssembly()), "");
else
{
string assembly = _assembly;
for (auto const& replacement: _replacements)
assembly = boost::algorithm::replace_all_copy(assembly, replacement.first, replacement.second);
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(assembly, m_context.nonConstAssembly()), "");
}
}

}
}
7 changes: 7 additions & 0 deletions libsolidity/codegen/ExpressionCompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ class ExpressionCompiler: private ASTConstVisitor
/// @returns the CompilerUtils object containing the current context.
CompilerUtils utils();

/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
/// prior to parsing the inline assembly.
void appendInlineAssembly(
std::string const& _assembly,
std::map<std::string, std::string> const& _replacements = std::map<std::string, std::string>{}
);

bool m_optimize;
CompilerContext& m_context;
std::unique_ptr<LValue> m_currentLValue;
Expand Down
Loading