Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ jobs:
FUZZER_TIMEOUT_EXEC_MS: 500
AFL_NO_STARTUP_CALIBRATION: 1
AFL_CMPLOG_ONLY_NEW: 1
AFL_MAP_SIZE: 223723
AFL_MAP_SIZE: 312000
volumes:
- ${{ github.workspace }}:/src
options: --cpus 4
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
### Added
- the repl prints the output of the last expression it ran
- new super instructions: `MUL_BY`, `MUL_BY_INDEX`, `MUL_SET_VAL` that can do multiplications (and optional storing in vars) in place
- new super instruction: `FUSED_MATH`, which can fuse 2 to 3 math operations in one go (ADD, SUB, MUL, DIV)

### Fixed
- the REPL doesn't color `import` in two colors (red for `imp__t` and blue for `___or_`), it keeps the first color that matched (red for import here)
Expand Down
15 changes: 14 additions & 1 deletion include/Ark/Compiler/Instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,10 +429,22 @@ namespace Ark::internal
// @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false
LT_LEN_SYM_JUMP_IF_FALSE = 0x6b,

// @args symbol id, offset number
// @role Multiply the symbol by (offset symbol - 2048), then push it to the stack
MUL_BY = 0x6c,

// @args symbol index, offset number
// @role Multiply the symbol by (offset symbol - 2048), then push it to the stack
MUL_BY_INDEX = 0x6d,

// @args symbol id, offset number
// @role Multiply the symbol by (offset symbol - 2048), then store the result using the given symbol id
MUL_SET_VAL = 0x6e,

// @args op1, op2, op3
// @role Pop 3 or 4 values from the stack, and apply the ops sequentially (only ADD, SUB, MUL, and DIV are supported). Push the result to the stack. Only op3 may be NOP.
FUSED_MATH = 0x6f,

InstructionsCount
};

Expand Down Expand Up @@ -549,7 +561,8 @@ namespace Ark::internal
"LT_LEN_SYM_JUMP_IF_FALSE",
"MUL_BY",
"MUL_BY_INDEX",
"MUL_SET_VAL"
"MUL_SET_VAL",
"FUSED_MATH"
};

static_assert(InstructionNames.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instruction names appear to be missing");
Expand Down
8 changes: 7 additions & 1 deletion include/Ark/Compiler/IntermediateRepresentation/Entity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ namespace Ark::internal::IR
Goto,
GotoWithArg,
Opcode,
Opcode2Args
Opcode2Args,
Opcode3Args
};

using label_t = std::size_t;
Expand All @@ -45,6 +46,8 @@ namespace Ark::internal::IR

Entity(Instruction inst, uint16_t primary_arg, uint16_t secondary_arg);

Entity(Instruction inst, uint8_t inst2, uint8_t inst3, uint8_t inst4);

static Entity Label(label_t value);

static Entity Goto(const Entity& label, Instruction inst = Instruction::JUMP);
Expand All @@ -65,6 +68,8 @@ namespace Ark::internal::IR

[[nodiscard]] inline uint16_t secondaryArg() const { return m_secondary_arg; }

[[nodiscard]] inline uint16_t tertiaryArg() const { return m_tertiary_arg; }

void setSourceLocation(const std::string& filename, std::size_t line);

[[nodiscard]] inline bool hasValidSourceLocation() const { return !m_source_file.empty(); }
Expand All @@ -79,6 +84,7 @@ namespace Ark::internal::IR
Instruction m_inst { NOP };
uint16_t m_primary_arg { 0 };
uint16_t m_secondary_arg { 0 };
uint16_t m_tertiary_arg { 0 };
std::string m_source_file;
std::size_t m_source_line { 0 };
};
Expand Down
4 changes: 4 additions & 0 deletions include/Ark/Compiler/IntermediateRepresentation/Word.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ namespace Ark::internal
byte_2 = static_cast<uint8_t>((secondary_arg & 0x00f) << 4 | (primary_arg & 0xf00) >> 8);
byte_3 = static_cast<uint8_t>(primary_arg & 0x0ff);
}

Word(const uint8_t inst, const uint8_t one, const uint8_t two, const uint8_t three) :
opcode(inst), byte_1(one), byte_2(two), byte_3(three)
{}
};
}

Expand Down
3 changes: 3 additions & 0 deletions src/arkreactor/Compiler/AST/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,9 @@ namespace Ark::internal
else
return std::nullopt;

if (func.value().nodeType() == NodeType::Symbol && func.value().string() == "ref")
error("`ref' can not be used outside a function's arguments list.", func_name_pos);

std::optional<Node> leaf { NodeType::List };
leaf->push_back(positioned(func.value(), func_name_pos));

Expand Down
9 changes: 7 additions & 2 deletions src/arkreactor/Compiler/BytecodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ namespace Ark
SymRaw, ///< Symbol, number
RawSym, ///< Symbol index, symbol
RawConst, ///< Symbol index, constant
RawRaw ///< Symbol index, symbol index
RawRaw, ///< Symbol index, symbol index
RawRawRaw
};

struct Arg
Expand Down Expand Up @@ -562,7 +563,8 @@ namespace Ark
{ LT_LEN_SYM_JUMP_IF_FALSE, ArgKind::SymRaw },
{ MUL_BY, ArgKind::RawRaw },
{ MUL_BY_INDEX, ArgKind::RawRaw },
{ MUL_SET_VAL, ArgKind::RawRaw }
{ MUL_SET_VAL, ArgKind::RawRaw },
{ FUSED_MATH, ArgKind::RawRawRaw }
};

const auto builtin_name = [](const uint16_t idx) {
Expand Down Expand Up @@ -627,6 +629,9 @@ namespace Ark
case ArgKind::RawRaw:
fmt::print(" {}, {}\n", fmt::styled(arg->primary(), raw_color), fmt::styled(arg->secondary(), raw_color));
break;
case ArgKind::RawRawRaw:
fmt::print(" {}, {}, {}\n", fmt::styled(arg->padding, raw_color), fmt::styled((arg->arg & 0xff00) >> 8, raw_color), fmt::styled(arg->arg & 0x00ff, raw_color));
break;
}
}
else
Expand Down
11 changes: 11 additions & 0 deletions src/arkreactor/Compiler/IntermediateRepresentation/Entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ namespace Ark::internal::IR
m_inst(inst), m_primary_arg(primary_arg), m_secondary_arg(secondary_arg)
{}

Entity::Entity(const Instruction inst, const uint8_t inst2, const uint8_t inst3, const uint8_t inst4) :
m_kind(Kind::Opcode3Args),
m_inst(inst), m_primary_arg(inst2), m_secondary_arg(inst3), m_tertiary_arg(inst4)
{}

Entity Entity::Label(const label_t value)
{
auto entity = Entity(Kind::Label);
Expand Down Expand Up @@ -55,6 +60,12 @@ namespace Ark::internal::IR
return Word(m_inst, m_primary_arg);
if (m_kind == Kind::Opcode2Args)
return Word(m_inst, m_primary_arg, m_secondary_arg);
if (m_kind == Kind::Opcode3Args)
return Word(
m_inst,
static_cast<uint8_t>(m_primary_arg),
static_cast<uint8_t>(m_secondary_arg),
static_cast<uint8_t>(m_tertiary_arg));
return Word(0, 0);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ namespace Ark::internal
case IR::Kind::Opcode2Args:
fmt::println(stream, "\t{} {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg());
break;

case IR::Kind::Opcode3Args:
fmt::println(stream, "\t{} {}, {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg(), entity.tertiaryArg());
break;
}
}

Expand Down Expand Up @@ -158,6 +162,8 @@ namespace Ark::internal
case IR::Kind::Opcode:
[[fallthrough]];
case IR::Kind::Opcode2Args:
[[fallthrough]];
case IR::Kind::Opcode3Args:
pushWord(inst.bytecode());
break;

Expand Down
28 changes: 28 additions & 0 deletions src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@

namespace Ark::internal
{
IR::Entity fuseMathOps3(const std::span<const IR::Entity> e)
{
return IR::Entity(FUSED_MATH, e[0].inst(), e[1].inst(), e[2].inst());
}

IR::Entity fuseMathOps2(const std::span<const IR::Entity> e)
{
return IR::Entity(FUSED_MATH, e[0].inst(), e[1].inst(), NOP);
}

IROptimizer::IROptimizer(const unsigned debug) :
m_logger("IROptimizer", debug)
{
Expand Down Expand Up @@ -234,6 +244,24 @@ namespace Ark::internal
return IR::Entity::GotoWithArg(e[3], LT_LEN_SYM_JUMP_IF_FALSE, e[0].primaryArg());
} },
};

const auto math_ops = { ADD, SUB, MUL, DIV };
for (const auto& one : math_ops)
{
for (const auto& two : math_ops)
{
for (const auto& three : math_ops)
m_ruleset.emplace_back(Rule { { one, two, three }, fuseMathOps3 });
}
}

for (const auto& one : math_ops)
{
for (const auto& two : math_ops)
m_ruleset.emplace_back(Rule { { one, two }, fuseMathOps2 });
}

m_logger.debug("Loaded {} rules", m_ruleset.size());
}

void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
Expand Down
83 changes: 81 additions & 2 deletions src/arkreactor/VM/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,37 @@ namespace Ark
types::Contract { { types::Typedef("src", ValueType::String), types::Typedef("idx", ValueType::Number) } } } },
{ container, index });
}

inline double doMath(double a, double b, const Instruction op)
{
if (op == ADD)
a += b;
else if (op == SUB)
a -= b;
else if (op == MUL)
a *= b;
else if (op == DIV)
{
if (b == 0)
Ark::VM::throwVMError(ErrorKind::DivisionByZero, fmt::format("Can not compute expression (/ {} {})", a, b));
a /= b;
}

return a;
}

inline std::string mathInstToStr(const Instruction op)
{
if (op == ADD)
return "+";
if (op == SUB)
return "-";
if (op == MUL)
return "*";
if (op == DIV)
return "/";
return "???";
}
}

VM::VM(State& state) noexcept :
Expand Down Expand Up @@ -647,7 +678,8 @@ namespace Ark
&&TARGET_LT_LEN_SYM_JUMP_IF_FALSE,
&&TARGET_MUL_BY,
&&TARGET_MUL_BY_INDEX,
&&TARGET_MUL_SET_VAL
&&TARGET_MUL_SET_VAL,
&&TARGET_FUSED_MATH
};

static_assert(opcode_targets.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instructions are not implemented in the VM");
Expand Down Expand Up @@ -713,7 +745,7 @@ namespace Ark
{
// Not resolving a potential ref is on purpose!
// This instruction is only used by functions when storing arguments
Value* tmp = pop(context);
const Value* tmp = pop(context);
store(arg, tmp, context);
DISPATCH();
}
Expand Down Expand Up @@ -2051,6 +2083,53 @@ namespace Ark
}
DISPATCH();
}

TARGET(FUSED_MATH)
{
const auto op1 = static_cast<Instruction>(padding),
op2 = static_cast<Instruction>((arg & 0xff00) >> 8),
op3 = static_cast<Instruction>(arg & 0x00ff);
const std::size_t arg_count = (op1 != NOP) + (op2 != NOP) + (op3 != NOP);

const Value* d = popAndResolveAsPtr(context);
const Value* c = popAndResolveAsPtr(context);
const Value* b = popAndResolveAsPtr(context);

if (d->valueType() != ValueType::Number || c->valueType() != ValueType::Number)
throw types::TypeCheckingError(
helper::mathInstToStr(op1),
{ { types::Contract { { types::Typedef("a", ValueType::Number), types::Typedef("b", ValueType::Number) } } } },
{ *c, *d });

double temp = helper::doMath(c->number(), d->number(), op1);
if (b->valueType() != ValueType::Number)
throw types::TypeCheckingError(
helper::mathInstToStr(op2),
{ { types::Contract { { types::Typedef("a", ValueType::Number), types::Typedef("b", ValueType::Number) } } } },
{ *b, Value(temp) });
temp = helper::doMath(b->number(), temp, op2);

if (arg_count == 2)
push(Value(temp), context);
else if (arg_count == 3)
{
const Value* a = popAndResolveAsPtr(context);
if (a->valueType() != ValueType::Number)
throw types::TypeCheckingError(
helper::mathInstToStr(op3),
{ { types::Contract { { types::Typedef("a", ValueType::Number), types::Typedef("b", ValueType::Number) } } } },
{ *a, Value(temp) });

temp = helper::doMath(a->number(), temp, op3);
push(Value(temp), context);
}
else
throw Error(
fmt::format(
"FUSED_MATH got {} arguments, expected 2 or 3. Arguments: {:x}{:x}{:x}. There is a bug in the codegen!",
arg_count, static_cast<uint8_t>(op1), static_cast<uint8_t>(op2), static_cast<uint8_t>(op3)));
DISPATCH();
}
#pragma endregion
}
#if ARK_USE_COMPUTED_GOTOS
Expand Down
Loading
Loading