[Assembler] Fix immediate size checking
MODIFY
src/assembler/instruction.h
src/assembler/instruction.h
@@ -127,8 +127,8 @@ struct Reg : public Field {
struct ImmPart {
ImmPart(unsigned _offset, BitRange _range) : offset(_offset), range(_range) {}
ImmPart(unsigned _offset, unsigned _start, unsigned _stop) : offset(_offset), range({_start, _stop}) {}
- void apply(const uint32_t& value, uint32_t& instruction) const { instruction |= range.apply(value >> offset); }
- void decode(uint32_t& value, const uint32_t& instruction) const { value |= range.decode(instruction) << offset; };
+ void apply(const uint32_t value, uint32_t& instruction) const { instruction |= range.apply(value >> offset); }
+ void decode(uint32_t& value, const uint32_t instruction) const { value |= range.decode(instruction) << offset; };
const unsigned offset;
const BitRange range;
};
@@ -152,26 +152,31 @@ struct Imm : public Field {
const QString& immToken = line.tokens[tokenIndex];
// Accept base 10, 16 and 2
- uint32_t value;
- value = immToken.toUInt(&success, 10);
+ uint32_t uvalue;
+ int32_t svalue;
+ if (repr == Repr::Signed) {
+ svalue = immToken.toInt(&success, 10);
+ } else {
+ uvalue = immToken.toUInt(&success, 10);
+ }
if (!success && immToken.toUpper().startsWith(QStringLiteral("0X"))) {
- value = immToken.toUInt(&success, 16);
+ uvalue = immToken.toUInt(&success, 16);
}
if (!success && immToken.toUpper().startsWith(QStringLiteral("0B"))) {
- value = immToken.toUInt(&success, 2);
+ uvalue = immToken.toUInt(&success, 2);
}
if (!success) {
return AssemblerTmp::Error(line.sourceLine, "Malformed immediate value '" + immToken + "'");
}
- if (!valueFitsInBitWidth(width, static_cast<int32_t>(value))) {
+ if (!((repr == Repr::Signed && isInt(width, svalue)) || (isUInt(width, uvalue)))) {
return AssemblerTmp::Error(line.sourceLine, "Immediate value '" + immToken + "' does not fit in " +
QString::number(width) + " bits");
}
for (const auto& part : parts) {
- part.apply(value, instruction);
+ part.apply(repr == Repr::Signed ? static_cast<uint32_t>(svalue) : uvalue, instruction);
}
return {};
}

MODIFY
src/binutils.h
src/binutils.h
@@ -38,6 +38,14 @@ constexpr inline std::enable_if_t<(N < 64), bool> isUInt(uint64_t X) {
return X < (UINT64_C(1) << (N));
}
+// Runtime versions of the above
+constexpr inline bool isUInt(unsigned N, uint64_t X) {
+ return X < (UINT64_C(1) << (N));
+}
+constexpr inline bool isInt(unsigned N, int64_t x) {
+ return N >= 64 || (-(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1)));
+}
+
constexpr uint32_t generateBitmask(int n) {
return static_cast<uint32_t>((1 << n) - 1);
}
@@ -66,10 +74,4 @@ constexpr inline unsigned ceillog2(unsigned x) {
return x == 1 || x == 0 ? 1 : floorlog2(x - 1) + 1;
}
-constexpr bool valueFitsInBitWidth(unsigned int width, int32_t value) {
- const int v = value < 0 ? -value : value;
- unsigned v_width = ceillog2(v) + ((bitcount(value) == 1) && (value != 0) && (value != 1) ? 1 : 0);
- return v_width <= width;
-}
-
} // namespace Ripes

MODIFY
test/tst_assembler.cpp
test/tst_assembler.cpp
@@ -15,11 +15,13 @@ class tst_Assembler : public QObject {
private slots:
void tst_simpleprogram();
+ void tst_simpleWithBranch();
void tst_segment();
void tst_matcher();
void tst_label();
void tst_labelWithPseudo();
void tst_weirdImmediates();
+ void tst_edgeImmediates();
private:
std::vector<std::shared_ptr<Instruction<RVISA>>> createInstructions();
@@ -39,12 +41,29 @@ void tst_Assembler::tst_simpleprogram() {
return;
}
+void tst_Assembler::tst_simpleWithBranch() {
+ auto assembler = RV32I_Assembler({});
+ QStringList program = QStringList() << "addi a0 a0 10"
+ << "B:"
+ << "addi a0 a0 -1"
+ << "beqz a0 B";
+ auto res = assembler.assemble(program);
+ if (res.errors.size() != 0) {
+ res.errors.print();
+ }
+ auto disres = assembler.disassemble(res.program);
+
+ return;
+}
+
void tst_Assembler::tst_weirdImmediates() {
auto assembler = RV32I_Assembler({});
QStringList program = QStringList() << "addi a0 a0 0q1234"
<< "addi a0 a0 -abcd"
<< "addi a0 a0 100000000"
- << "addi a0 a0 4096"
+ << "addi a0 a0 4096" // too large
+ << "addi a0 a0 2048" // too large
+ << "addi a0 a0 -2049" // too large
<< "addi a0 a0 0xabcdabcdabcd";
auto res = assembler.assemble(program);
if (res.errors.size() != 0) {
@@ -54,6 +73,19 @@ void tst_Assembler::tst_weirdImmediates() {
return;
}
+void tst_Assembler::tst_edgeImmediates() {
+ auto assembler = RV32I_Assembler({});
+ QStringList program = QStringList() << "addi a0 a0 2047"
+ << "addi a0 a0 -2048";
+ auto res = assembler.assemble(program);
+ if (res.errors.size() != 0) {
+ res.errors.print();
+ QFAIL("Expected no errors");
+ }
+
+ return;
+}
+
void tst_Assembler::tst_label() {
auto assembler = RV32I_Assembler({});
QStringList program = QStringList() << "A:"

[Assembler] Fix immediate size checking