summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2023-05-27 18:10:42 +0300
committerOxore <oxore@protonmail.com>2023-05-27 18:12:31 +0300
commita4841d2a593f9efed1cb116137034c307c1d74bc (patch)
treeee0e850b39f6b7dfe1532835409d88e2e33156ef
parentedfe901ba751f4fcdf287102037eb36bf28cfa2a (diff)
Impl -fshort-ref-local-labels, refactor feature parsing
-rw-r--r--common.h1
-rw-r--r--disasm.cpp37
-rw-r--r--disasm.h3
-rw-r--r--main.cpp199
-rw-r--r--test_labels_referencing.bash10
-rw-r--r--test_random.bash2
6 files changed, 176 insertions, 76 deletions
diff --git a/common.h b/common.h
index 36a5ba3..73689ba 100644
--- a/common.h
+++ b/common.h
@@ -8,6 +8,7 @@ struct Settings {
bool rel_labels{};
bool abs_labels{};
bool imm_labels{};
+ bool short_ref_local_labels{};
bool export_labels{};
bool export_all_labels{};
bool export_functions{};
diff --git a/disasm.cpp b/disasm.cpp
index 6179c8d..20e4ec8 100644
--- a/disasm.cpp
+++ b/disasm.cpp
@@ -1822,6 +1822,7 @@ int Arg::SNPrint(
const size_t bufsz,
const bool imm_as_hex,
const RefKindMask ref_kinds,
+ const char *const label,
const uint32_t self_addr,
const uint32_t ref_addr) const
{
@@ -1857,11 +1858,11 @@ int Arg::SNPrint(
const char c = type == ArgType::kLong ? 'l' : 'w';
if (ref_kinds & kRefAbsMask) {
if (static_cast<uint32_t>(lword) == ref_addr) {
- return snprintf(buf, bufsz, "L%08x:%c", ref_addr, c);
+ return snprintf(buf, bufsz, "%s:%c", label, c);
} else {
// It has to be AFTER the label we are gonna reference here
assert(static_cast<uint32_t>(lword) > ref_addr);
- return snprintf(buf, bufsz, "L%08x+%d:%c", ref_addr, lword - ref_addr, c);
+ return snprintf(buf, bufsz, "%s+%d:%c", label, lword - ref_addr, c);
}
} else {
return snprintf(buf, bufsz, "0x%x:%c", lword, c);
@@ -1874,10 +1875,10 @@ int Arg::SNPrint(
const bool has_fix = ref_kinds & kRefPcRelFix2Bytes;
const uint32_t arg_addr = self_addr + d16_pc.d16 + kInstructionSizeStepBytes + (has_fix ? kInstructionSizeStepBytes : 0);
if (arg_addr == ref_addr) {
- return snprintf(buf, bufsz, "%%pc@(L%08x:w)", ref_addr);
+ return snprintf(buf, bufsz, "%%pc@(%s:w)", label);
} else {
assert(arg_addr > ref_addr);
- return snprintf(buf, bufsz, "%%pc@(L%08x+%d:w)", ref_addr, arg_addr - ref_addr);
+ return snprintf(buf, bufsz, "%%pc@(%s+%d:w)", label, arg_addr - ref_addr);
}
} else {
return snprintf(buf, bufsz, "%%pc@(%d:w)", d16_pc.d16);
@@ -1892,11 +1893,11 @@ int Arg::SNPrint(
case ArgType::kImmediate:
if (ref_kinds & kRef1ImmMask) {
if (static_cast<uint32_t>(lword) == ref_addr) {
- return snprintf(buf, bufsz, "#L%08x", ref_addr);
+ return snprintf(buf, bufsz, "#%s", label);
} else {
// It has to be AFTER the label we are gonna reference here
assert(static_cast<uint32_t>(lword) > ref_addr);
- return snprintf(buf, bufsz, "#L%08x+%d", ref_addr, lword - ref_addr);
+ return snprintf(buf, bufsz, "#%s+%d", label, lword - ref_addr);
}
} else if (imm_as_hex) {
return snprintf(buf, bufsz, "#0x%x", lword);
@@ -1909,10 +1910,10 @@ int Arg::SNPrint(
case ArgType::kDisplacement:
if (ref_kinds & kRefRelMask) {
if (static_cast<uint32_t>(self_addr + lword) == ref_addr) {
- return snprintf(buf, bufsz, "L%08x", ref_addr);
+ return snprintf(buf, bufsz, "%s", label);
} else {
assert(static_cast<uint32_t>(self_addr + lword) > ref_addr);
- return snprintf(buf, bufsz, "L%08x+%d", ref_addr, (self_addr + lword) - ref_addr);
+ return snprintf(buf, bufsz, "%s+%d", label, (self_addr + lword) - ref_addr);
}
} else {
return snprintf(buf, bufsz, ".%s%d", lword >= 0 ? "+" : "", lword);
@@ -1932,6 +1933,8 @@ int Op::FPrint(
FILE *const stream,
const char *const indent,
const RefKindMask ref_kinds,
+ const char *const ref1_label,
+ const char *const ref2_label,
const uint32_t self_addr,
const uint32_t ref1_addr,
const uint32_t ref2_addr) const
@@ -1950,11 +1953,25 @@ int Op::FPrint(
arg2.type == ArgType::kAn ||
arg2.type == ArgType::kCCR ||
arg2.type == ArgType::kSR;
- arg1.SNPrint(arg1_str, kArgsBufferSize, imm_as_hex, ref1_kinds, self_addr, ref1_addr);
+ arg1.SNPrint(
+ arg1_str,
+ kArgsBufferSize,
+ imm_as_hex,
+ ref1_kinds,
+ ref1_label,
+ self_addr,
+ ref1_addr);
if (arg2.type != ArgType::kNone) {
char arg2_str[kArgsBufferSize]{};
const RefKindMask ref2_kinds = ref_kinds & (kRef2Mask | kRefPcRelFix2Bytes);
- arg2.SNPrint(arg2_str, kArgsBufferSize, false, ref2_kinds, self_addr, ref2_addr);
+ arg2.SNPrint(
+ arg2_str,
+ kArgsBufferSize,
+ false,
+ ref2_kinds,
+ ref2_label,
+ self_addr,
+ ref2_addr);
return fprintf(stream, "%s%s %s,%s", indent, mnemonic_str, arg1_str, arg2_str);
} else {
return fprintf(stream, "%s%s %s", indent, mnemonic_str, arg1_str);
diff --git a/disasm.h b/disasm.h
index 441f7da..f138e5f 100644
--- a/disasm.h
+++ b/disasm.h
@@ -299,6 +299,7 @@ struct Arg {
size_t bufsz,
bool imm_as_hex = false,
RefKindMask ref_kinds = 0,
+ const char *label = nullptr,
uint32_t self_addr = 0,
uint32_t ref_addr = 0) const;
};
@@ -355,6 +356,8 @@ struct Op {
FILE *,
const char *indent,
RefKindMask ref_kinds = 0,
+ const char *ref1_label = nullptr,
+ const char *ref2_label = nullptr,
uint32_t self_addr = 0,
uint32_t ref1_addr = 0,
uint32_t ref2_addr = 0) const;
diff --git a/main.cpp b/main.cpp
index e2e4191..84abd3f 100644
--- a/main.cpp
+++ b/main.cpp
@@ -26,7 +26,7 @@ enum class DisasmMapType {
class DisasmMap {
const DisasmMapType _type;
DisasmNode *_map[kDisasmMapSizeElements]{};
- DisasmNode *findNodeByOffset(uint32_t offset) const;
+ constexpr DisasmNode *findNodeByOffset(uint32_t offset) const;
DisasmNode *insertTracedNode(uint32_t offset, TracedNodeType);
void insertReferencedBy(
const uint32_t by_addr,
@@ -36,7 +36,7 @@ class DisasmMap {
const ReferenceType ref_type);
bool canBeAllocated(const DisasmNode& node) const;
public:
- const DisasmNode *FindNodeByOffset(uint32_t offset) const
+ constexpr const DisasmNode *FindNodeByOffset(uint32_t offset) const
{
return findNodeByOffset(offset);
};
@@ -52,7 +52,7 @@ public:
~DisasmMap();
};
-DisasmNode *DisasmMap::findNodeByOffset(uint32_t offset) const
+constexpr DisasmNode *DisasmMap::findNodeByOffset(uint32_t offset) const
{
if (offset < kRomSizeBytes)
return _map[offset / kInstructionSizeStepBytes];
@@ -258,6 +258,63 @@ static constexpr bool HasCallReference(const DisasmNode &node)
return false;
}
+static constexpr size_t GetNodeSizeByAddress(const DisasmMap &disasm_map, const uint32_t address)
+{
+ const auto *node = disasm_map.FindNodeByOffset(address);
+ if (node == nullptr) {
+ return kInstructionSizeStepBytes;
+ }
+ return node->size;
+}
+
+static constexpr bool IsLocalLocation(const DisasmMap &disasm_map, const DisasmNode &node)
+{
+ for (const ReferenceNode *ref{node.ref_by}; ref; ref = ref->next) {
+ for (size_t i = 0; i < ref->refs_count; i++) {
+ const ReferenceRecord &ref_rec = ref->refs[i];
+ if (ref_rec.type == ReferenceType::kCall) {
+ // Locals are definitely not made for calls
+ return false;
+ }
+ const bool forward = ref_rec.address < node.offset;
+ const size_t min_addr = forward ? ref_rec.address : node.offset;
+ const size_t start = min_addr + GetNodeSizeByAddress(disasm_map, min_addr);
+ const size_t max_addr = forward ? node.offset : ref_rec.address;
+ const size_t end = max_addr + (forward ? 0 : GetNodeSizeByAddress(disasm_map, min_addr));
+ for (size_t o = start; o < end;) {
+ const auto *intermediate_node = disasm_map.FindNodeByOffset(o);
+ if (intermediate_node) {
+ if (intermediate_node->ref_by) {
+ // Another labeled node detected on the jump path, hence
+ // current node's location cannot be considered local
+ return false;
+ }
+ o += intermediate_node->size;
+ } else {
+ o += kInstructionSizeStepBytes;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+static constexpr const char *StringWihoutFristNChars(const char *str, const size_t n)
+{
+ for (size_t i = 0, tab = 0; i < n && *str; i++, str++) {
+ if (*str == '\t') {
+ tab++;
+ if (tab == 7) {
+ tab = 0;
+ str++;
+ }
+ } else {
+ str++;
+ }
+ }
+ return str;
+}
+
static void RenderNodeDisassembly(
FILE *const output,
const DisasmMap &disasm_map,
@@ -266,7 +323,8 @@ static void RenderNodeDisassembly(
const DisasmNode &node)
{
if (node.ref_by) {
- if (s.labels) {
+ const bool is_local = IsLocalLocation(disasm_map, node);
+ if (s.labels && !(s.short_ref_local_labels && is_local)) {
const bool export_this_function = s.export_functions && HasCallReference(node);
const bool export_this_label = s.export_all_labels ||
(s.export_labels && node.ref_by && (node.ref_by->refs_count > 1)) ||
@@ -278,7 +336,7 @@ static void RenderNodeDisassembly(
}
}
}
- if (s.xrefs_from) {
+ if (s.xrefs_from && !(s.short_ref_local_labels && is_local)) {
fprintf(output, "| XREFS:\n");
for (const ReferenceNode *ref{node.ref_by}; ref; ref = ref->next) {
if (ref->refs_count == 0) {
@@ -293,7 +351,11 @@ static void RenderNodeDisassembly(
}
}
if (s.labels) {
- fprintf(output, "L%08x:\n", node.offset);
+ if (s.short_ref_local_labels && is_local) {
+ fprintf(output, "1:%s", StringWihoutFristNChars(s.indent, (sizeof "1:") - 1));
+ } else {
+ fprintf(output, "L%08x:\n", node.offset);
+ }
}
}
assert(node.op.opcode != OpCode::kNone);
@@ -328,16 +390,40 @@ static void RenderNodeDisassembly(
: 0) |
((s.imm_labels && ref1) ? (node.ref_kinds & kRef1ImmMask) : 0) |
(node.ref_kinds & (kRefDataMask | kRefPcRelFix2Bytes));
- node.op.FPrint(output, s.indent, ref_kinds, node.offset, ref1_addr, ref2_addr);
- if (s.xrefs_to && ref1) {
- char ref_addr_str[12]{};
- snprintf(ref_addr_str, (sizeof ref_addr_str), "L%08x", ref1_addr);
- fprintf(output, " | %s", ref_addr_str);
+ const bool ref1_is_local = !ref1 || IsLocalLocation(disasm_map, *ref1);
+ char ref1_label[32]{};
+ if (ref1) {
+ if (s.short_ref_local_labels && ref1_is_local) {
+ const char dir = ref1_addr <= node.offset ? 'b' : 'f';
+ snprintf(ref1_label, (sizeof ref1_label), "1%c", dir);
+ } else {
+ snprintf(ref1_label, (sizeof ref1_label), "L%08x", ref1_addr);
+ }
+ }
+ const bool ref2_is_local = !ref2 || IsLocalLocation(disasm_map, *ref2);
+ char ref2_label[32]{};
+ if (ref2) {
+ if (s.short_ref_local_labels && ref2_is_local) {
+ const char dir = ref2_addr <= node.offset ? 'b' : 'f';
+ snprintf(ref2_label, (sizeof ref2_label), "1%c", dir);
+ } else {
+ snprintf(ref2_label, (sizeof ref2_label), "L%08x", ref2_addr);
+ }
}
- if (s.xrefs_to && ref2) {
- char ref_addr_str[12]{};
- snprintf(ref_addr_str, (sizeof ref_addr_str), "L%08x", ref2_addr);
- fprintf(output, " | %s", ref_addr_str);
+ node.op.FPrint(
+ output,
+ s.indent,
+ ref_kinds,
+ ref1_label,
+ ref2_label,
+ node.offset,
+ ref1_addr,
+ ref2_addr);
+ if (s.xrefs_to && !(s.short_ref_local_labels && ref1_is_local)) {
+ fprintf(output, " | L%08x", ref1_addr);
+ }
+ if (s.xrefs_to && !(s.short_ref_local_labels && ref2_is_local)) {
+ fprintf(output, " | L%08x", ref2_addr);
}
} else {
node.op.FPrint(output, s.indent);
@@ -495,62 +581,44 @@ static int M68kDisasmAll(FILE *input_stream, FILE *output_stream, const Settings
return EXIT_SUCCESS;
}
-static bool IsValidFeature(const char *feature)
+static bool FeatureStringHasPrefixNo(const char *feature)
{
- constexpr size_t sizeof_no_prefix = sizeof("no-")-1;
- if (0 == memcmp(feature, "no-", sizeof_no_prefix)) {
- feature += sizeof_no_prefix;
- }
- if (0 == strcmp(feature, "rdc")) {
- return true;
- } else if (0 == strcmp(feature, "labels")) {
- return true;
- } else if (0 == strcmp(feature, "rel-labels")) {
- return true;
- } else if (0 == strcmp(feature, "abs-labels")) {
- return true;
- } else if (0 == strcmp(feature, "imm-labels")) {
- return true;
- } else if (0 == strcmp(feature, "export-labels")) {
- return true;
- } else if (0 == strcmp(feature, "export-all-labels")) {
- return true;
- } else if (0 == strcmp(feature, "export-functions")) {
- return true;
- } else if (0 == strcmp(feature, "xrefs-from")) {
- return true;
- } else if (0 == strcmp(feature, "xrefs-to")) {
+ assert(feature);
+ // There is also implicit, embedded and free check for null terminator
+ if (feature[0] == 'n' && feature[1] == 'o' && feature[2] == '-') {
return true;
}
return false;
}
-static void ApplyFeature(Settings& s, const char *feature_arg)
+static bool ApplyFeature(Settings& s, const char *feature_arg)
{
+ struct {
+ bool Settings::* setting;
+ const char* feature_name;
+ } const features[]{
+ { &Settings::raw_data_comment, "rdc" },
+ { &Settings::labels, "labels" },
+ { &Settings::rel_labels, "rel-labels" },
+ { &Settings::abs_labels, "abs-labels" },
+ { &Settings::imm_labels, "imm-labels" },
+ { &Settings::short_ref_local_labels, "short-ref-local-labels" },
+ { &Settings::export_labels, "export-labels" },
+ { &Settings::export_all_labels, "export-all-labels" },
+ { &Settings::export_functions, "export-functions" },
+ { &Settings::xrefs_from, "xrefs-from" },
+ { &Settings::xrefs_to, "xrefs-to" },
+ };
constexpr size_t sizeof_no_prefix = (sizeof "no-") - 1;
- const bool disable = (0 == memcmp(feature_arg, "no-", sizeof_no_prefix));
+ const bool disable = FeatureStringHasPrefixNo(feature_arg);
const char *const feature = feature_arg + (disable ? sizeof_no_prefix : 0);
- if (0 == strcmp(feature, "rdc")) {
- s.raw_data_comment = !disable;
- } else if (0 == strcmp(feature, "labels")) {
- s.labels = !disable;
- } else if (0 == strcmp(feature, "rel-labels")) {
- s.rel_labels = !disable;
- } else if (0 == strcmp(feature, "abs-labels")) {
- s.abs_labels = !disable;
- } else if (0 == strcmp(feature, "imm-labels")) {
- s.imm_labels = !disable;
- } else if (0 == strcmp(feature, "export-labels")) {
- s.export_labels = !disable;
- } else if (0 == strcmp(feature, "export-all-labels")) {
- s.export_all_labels = !disable;
- } else if (0 == strcmp(feature, "export-functions")) {
- s.export_functions = !disable;
- } else if (0 == strcmp(feature, "xrefs-from")) {
- s.xrefs_from = !disable;
- } else if (0 == strcmp(feature, "xrefs-to")) {
- s.xrefs_to = !disable;
+ for (size_t i = 0; i < (sizeof features) / (sizeof *features); i++) {
+ if (0 == strcmp(feature, features[i].feature_name)) {
+ s.*(features[i].setting) = !disable;
+ return true;
+ }
}
+ return false;
}
static void PrintUsage(FILE *s, const char *argv0)
@@ -576,6 +644,10 @@ static void PrintUsage(FILE *s, const char *argv0)
fprintf(s, " abs-labels Use label instead of number on absolute branch or call.\n");
fprintf(s, " imm-labels Use label instead of number when immediate value moved\n");
fprintf(s, " to address register.\n");
+ fprintf(s, " short-ref-local-labels\n");
+ fprintf(s, " Use local labels (numbers) for short jumps or loops.\n");
+ fprintf(s, " Jump is considered short when it does not cross other\n");
+ fprintf(s, " labels and has no calls.\n");
fprintf(s, " export-labels Add `.globl` preamble to labels referenced two or more\n");
fprintf(s, " times.\n");
fprintf(s, " export-all-labels Add `.globl` preamble to all labels.\n");
@@ -591,7 +663,7 @@ int main(int, char* argv[])
{"help", 'h', OPTPARSE_NONE},
{"output", 'o', OPTPARSE_REQUIRED},
{"pc-trace", 't', OPTPARSE_REQUIRED},
- {"feature", 'f', OPTPARSE_OPTIONAL},
+ {"feature", 'f', OPTPARSE_REQUIRED},
{"indent", 80, OPTPARSE_REQUIRED},
{},
};
@@ -616,11 +688,10 @@ int main(int, char* argv[])
trace_file_name = options.optarg;
break;
case 'f':
- if (!IsValidFeature(options.optarg)) {
+ if (!ApplyFeature(s, options.optarg)) {
fprintf(stderr, "main: Error: Unknown feature \"%s\", exiting\n", options.optarg);
return EXIT_FAILURE;
}
- ApplyFeature(s, options.optarg);
break;
case 80:
s.indent = options.optarg;
diff --git a/test_labels_referencing.bash b/test_labels_referencing.bash
index 0cedc13..ffb66ca 100644
--- a/test_labels_referencing.bash
+++ b/test_labels_referencing.bash
@@ -49,7 +49,7 @@ run_test_r() {
fi
local run_check=$4
$run_check
- echo && cat ${file_asm}
+ #echo && cat ${file_asm}
echo -e "${CGREEN}OK${CRST}"
}
@@ -69,6 +69,8 @@ run_check_r() {
fi
}
+run_check_dummy() { :; }
+
run_test_rdisp() {
run_test_r "$1" "$2" "-flabels -frel-labels" run_check_rdisp
}
@@ -81,6 +83,10 @@ run_test_rpcrel() {
run_test_r "$1" "$2" "-flabels -frel-labels" run_check_r
}
+run_test_rlocal() {
+ run_test_r "$1" "$2" "-flabels -frel-labels -fabs-labels -fshort-ref-local-labels" run_check_dummy
+}
+
run_test_rdisp "bras ." "\x60\xfe"
run_test_rdisp "bras .-2" "\x4e\x71\x60\xfc"
run_test_rdisp "bras .-1" "\x4e\x71\x60\xfd"
@@ -100,3 +106,5 @@ run_test_rword "cmpl 0x4:w, D2 with nop" "\xb4\xb8\x00\x04\x4e\x71"
run_test_rword "cmpw 0x0:l, D2" "\xb4\x79\x00\x00\x00\x00"
run_test_rpcrel "cmpl (0,PC), D2" "\xb4\xba\x00\x00"
run_test_rpcrel "cmpl (-2,PC), D2" "\xb4\xba\xff\xfe"
+run_test_rlocal "bras 1f; nop; 1: bras 1b" "\x60\x02\x4e\x71\x60\xfe"
+run_test_rlocal "2: bras 1f; nop; 1: bras 2b" "\x60\x02\x4e\x71\x60\xfa"
diff --git a/test_random.bash b/test_random.bash
index 9bc318f..735cd86 100644
--- a/test_random.bash
+++ b/test_random.bash
@@ -7,7 +7,7 @@
AS=m68k-none-elf-as
OBJCOPY=m68k-none-elf-objcopy
LD="m68k-none-elf-ld -Ttest.ld"
-DISASM="./cmake-build/m68k-disasm -fabs-labels -frel-labels -flabels -frdc"
+DISASM="./cmake-build/m68k-disasm -frdc -fxrefs-to -fxrefs-from -flabels -frel-labels -fabs-labels -fshort-ref-local-labels"
TEST_DIR=/tmp/m68k-disasm-random-tests
set -e