From 461e6110efb699f2465e5a8a25d93f48e6ee40ce Mon Sep 17 00:00:00 2001 From: Matt Godbolt Date: Tue, 20 Sep 2016 09:09:47 -0500 Subject: [PATCH] Fix unused label issues Treat labels which are used only by directives in other labels as used if the other label itself is used by an opcode: that is, propagate the transitive usage of labels. Fixes #35 --- lib/asm.js | 89 +++++++++++++++---- package.json | 3 +- test/cases/string-constant.asm | 46 ++++++++++ ...ng-constant.asm.directives.labels.comments | 17 ++++ test/test.js | 2 +- 5 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 test/cases/string-constant.asm create mode 100644 test/cases/string-constant.asm.directives.labels.comments diff --git a/lib/asm.js b/lib/asm.js index 9c513544..0405c5c2 100644 --- a/lib/asm.js +++ b/lib/asm.js @@ -23,6 +23,7 @@ // POSSIBILITY OF SUCH DAMAGE. (function () { + var _ = require('underscore-node'); var tabsRe = /\t/g; function expandTabs(line) { @@ -35,35 +36,91 @@ }); } - function processAsm(asm, filters) { - if (filters.binary) return processBinaryAsm(asm, filters); + var labelFind = /[.a-zA-Z_][a-zA-Z0-9$_.]*/g; + var dataDefn = /\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)/; + var fileFind = /^\s*\.file\s+(\d+)\s+"([^"]+)".*/; + var hasOpcode = /^\s*([a-zA-Z$_][a-zA-Z0-9$_.]*:\s*)?[a-zA-Z].*/; + var labelDef = /^([.a-zA-Z_][a-zA-Z0-9$_.]+):/; + var hasOpcode = /^\s*([a-zA-Z$_][a-zA-Z0-9$_.]*:\s*)?[a-zA-Z].*/; - var result = []; - var asmLines = asm.split("\n"); + function findUsedLabels(asmLines, filterDirectives) { var labelsUsed = {}; - var labelFind = /[.a-zA-Z0-9_][a-zA-Z0-9$_.]*/g; - var files = {}; - var prevLabel = ""; - var dataDefn = /\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)/; - var fileFind = /^\s*\.file\s+(\d+)\s+"([^"]+)".*/; - var hasOpcode = /^\s*([a-zA-Z0-9$_][a-zA-Z0-9$_.]*:\s*)?[a-zA-Z].*/; + var weakUsages = {}; + var currentLabel = ""; + + // Scan through looking for definite label usages (ones used by opcodes), + // and ones that are weakly used: that is, their use is conditional on another label. + // For example: + // .foo: .string "moo" + // .baz: .quad .foo + // mov eax, .baz + // In this case, the '.baz' is used by an opcode, and so is strongly used. + // The '.foo' is weakly used by .baz. asmLines.forEach(function (line) { - if (line === "" || line[0] === ".") return; - var match = line.match(labelFind); - if (match && (!filters.directives || line.match(hasOpcode))) { + var match = line.match(labelDef); + if (match) + currentLabel = match[1]; + if (!line || line[0] === '.') return; + + match = line.match(labelFind); + if (!match) return; + + if (!filterDirectives || line.match(hasOpcode)) { // Only count a label as used if it's used by an opcode, or else we're not filtering directives. match.forEach(function (label) { labelsUsed[label] = true; }); + } else if (currentLabel) { + // Note any "weak" usages by this label; that is, usages that are only + // interesting if the currentLabel is used by an opcode. + if (!weakUsages[currentLabel]) weakUsages[currentLabel] = []; + match.forEach(function (label) { + weakUsages[currentLabel].push(label); + }); } - match = line.match(fileFind); + }); + + // Now follow the chains of used labels, marking any weak references they refer + // to as also used. We iteratively do this until either no new labels are found, + // or we hit a limit (only here to prevent a pathological case from hanging). + var MaxLabelIterations = 10; + for (var iter = 0; iter < MaxLabelIterations; ++iter) { + var toAdd = []; + _.each(labelsUsed, function (t, label) { + _.each(weakUsages[label], function (nowused) { + if (labelsUsed[nowused]) return; + toAdd.push(nowused); + }); + }); + if (!toAdd) break; + _.each(toAdd, function (label) { + labelsUsed[label] = true; + }); + } + return labelsUsed; + } + + function parseFiles(asmLines) { + var files = {}; + asmLines.forEach(function (line) { + var match = line.match(fileFind); if (match) { files[parseInt(match[1])] = match[2]; } }); + return files; + } + + function processAsm(asm, filters) { + if (filters.binary) return processBinaryAsm(asm, filters); + + var result = []; + var asmLines = asm.split("\n"); + var labelsUsed = findUsedLabels(asmLines, filters.directives); + var files = parseFiles(asmLines); + var prevLabel = ""; var directive = /^\s*\..*$/; - var labelDefinition = /^([a-zA-Z0-9$_.]+):/; var commentOnly = /^\s*(#|@|\/\/).*/; var sourceTag = /^\s*\.loc\s+(\d+)\s+(\d+).*/; var stdInLooking = /.*|-/; @@ -89,7 +146,7 @@ if (filters.commentOnly && line.match(commentOnly)) return; - match = line.match(labelDefinition); + match = line.match(labelDef); if (match) { // It's a label definition. if (labelsUsed[match[1]] === undefined) { diff --git a/package.json b/package.json index e513e82b..eeca3ae9 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "nopt": "3.0.x", "serve-favicon": "2.3.x", "serve-static": "1.10.x", - "temp": "0.8.x" + "temp": "0.8.x", + "underscore-node": "*" }, "devDependencies": { "supervisor": "0.3.1", diff --git a/test/cases/string-constant.asm b/test/cases/string-constant.asm new file mode 100644 index 00000000..80452eed --- /dev/null +++ b/test/cases/string-constant.asm @@ -0,0 +1,46 @@ + .file "example.cpp" + .intel_syntax noprefix + .text +.Ltext0: + .globl s_sA + .section .rodata +.LC0: + .string "hello world!" + .data + .align 8 + .type s_sA, @object + .size s_sA, 8 +s_sA: + .quad .LC0 + .section .rodata + .align 8 + .type s_sB, @object + .size s_sB, 10 +s_sB: + .string "hey there" + .text + .globl main + .type main, @function +main: +.LFB0: + .file 1 "/tmp/gcc-explorer-compiler116820-58-ewfj5u/example.cpp" + .loc 1 6 0 + .cfi_startproc + push rbp + .cfi_def_cfa_offset 16 + .cfi_offset 6, -16 + mov rbp, rsp + .cfi_def_cfa_register 6 + .loc 1 7 0 + mov rax, QWORD PTR s_sA[rip] + mov rdi, rax + call puts + .loc 1 8 0 + mov edi, OFFSET FLAT:s_sB + call puts + .loc 1 9 0 + mov eax, 0 + pop rbp + .cfi_def_cfa 7, 8 + ret + .cfi_endproc diff --git a/test/cases/string-constant.asm.directives.labels.comments b/test/cases/string-constant.asm.directives.labels.comments new file mode 100644 index 00000000..5f6ea803 --- /dev/null +++ b/test/cases/string-constant.asm.directives.labels.comments @@ -0,0 +1,17 @@ +.LC0: + .string "hello world!" +s_sA: + .quad .LC0 +s_sB: + .string "hey there" +main: + push rbp + mov rbp, rsp + mov rax, QWORD PTR s_sA[rip] + mov rdi, rax + call puts + mov edi, OFFSET FLAT:s_sB + call puts + mov eax, 0 + pop rbp + ret diff --git a/test/test.js b/test/test.js index 2a626e25..531a7641 100755 --- a/test/test.js +++ b/test/test.js @@ -68,7 +68,6 @@ function testFilter(filename, suffix, filters, withSource) { assertEq(file[i], lineExpected, expected + ":" + (i + 1)); } } - cases.forEach(function (x) { testFilter(x, "", {}) }); @@ -91,4 +90,5 @@ cases.forEach(function (x) { if (failures) { console.log(failures + " failures"); + process.exit(1); }