Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #1194 from vcabbage/go-labels
go: replace PC jumps with labels
  • Loading branch information
partouf committed Dec 13, 2018
2 parents 950441b + d6d6055 commit e1dc829
Show file tree
Hide file tree
Showing 5 changed files with 635 additions and 21 deletions.
134 changes: 118 additions & 16 deletions lib/compilers/golang.js
Expand Up @@ -26,38 +26,140 @@ const BaseCompiler = require('../base-compiler'),
_ = require('underscore'),
utils = require('../utils');

// Each arch has a list of jump instructions in
// Go source src/cmd/asm/internal/arch.
const jumpPrefixes = [
'j',
'b',

// arm
'cb',
'tb',

// s390x
'cmpb',
'cmpub'
];

class GolangCompiler extends BaseCompiler {
convertNewGoL(code) {
const re = /^\s+(0[xX]?[0-9A-Za-z]+)?\s?[0-9]+\s*\(([^:]+):([0-9]+)\)\s*([A-Z]+)(.*)/;
const reUnknown = /^\s+(0[xX]?[0-9A-Za-z]+)?\s?[0-9]+\s*\(<unknown line number>\)\s*([A-Z]+)(.*)/;
const re = /^\s+(0[xX]?[0-9A-Za-z]+)?\s?([0-9]+)\s*\(([^:]+):([0-9]+)\)\s*([A-Z]+)(.*)/;
const reUnknown = /^\s+(0[xX]?[0-9A-Za-z]+)?\s?([0-9]+)\s*\(<unknown line number>\)\s*([A-Z]+)(.*)/;
const reFunc = /TEXT\s+[".]*(\S+)\(SB\)/;
let prevLine = null;
let file = null;
let fileCount = 0;
return _.compact(code.map(obj => {
let func = null;
let funcCollisions = {};
let labels = {};
let usedLabels = {};
let lines = code.map(obj => {
let pcMatch = null;
let fileMatch = null;
let lineMatch = null;
let ins = null;
let args = null;

const line = obj.text;
let match = line.match(re);
if (match) {
let res = "";
if (file !== match[2]) {
fileCount++;
res += "\t.file " + fileCount + ' "' + match[2] + '"\n';
file = match[2];
}
if (prevLine !== match[3]) {
res += "\t.loc " + fileCount + " " + match[3] + " 0\n";
prevLine = match[3];
}
return res + "\t" + match[4].toLowerCase() + match[5];
pcMatch = match[2];
fileMatch = match[3];
lineMatch = match[4];
ins = match[5];
args = match[6];
} else {
match = line.match(reUnknown);
if (match) {
return "\t" + match[2].toLowerCase() + match[3];
pcMatch = match[2];
ins = match[3];
args = match[4];
} else {
return null;
}
}

})).join("\n");
match = line.match(reFunc);
if (match) {
// Normalize function name.
func = match[1].replace(/[.()*]+/g, "_");

// It's possible for normalized function names to collide.
// Keep a count of collisions per function name. Labels get
// suffixed with _[collisions] when collisions > 0.
let collisions = funcCollisions[func];
if (collisions == null) {
collisions = 0;
} else {
collisions++;
}

funcCollisions[func] = collisions;
}

let res = [];
if (pcMatch && !labels[pcMatch]) {
// Create pseudo-label.
let label = pcMatch.replace(/^0{0,4}/, '');
let suffix = '';
if (funcCollisions[func] > 0) {
suffix = `_${funcCollisions[func]}`;
}

label = `${func}_pc${label}${suffix}:`;
if (!labels[label]) {
res.push(label);
labels[label] = true;
}
}

if (fileMatch && file !== fileMatch) {
fileCount++;
res.push(`\t.file ${fileCount} "${fileMatch}"`);
file = fileMatch;
}

if (lineMatch && prevLine !== lineMatch) {
res.push(`\t.loc ${fileCount} ${lineMatch} 0`);
prevLine = lineMatch;
}

ins = ins.toLowerCase();
args = this.replaceJump(func, funcCollisions[func], ins, args, usedLabels);
res.push(`\t${ins}${args}`);
return res;
});

// Find unused pseudo-labels so they can be filtered out.
let unusedLabels = _.mapObject(labels, (val, key) => { return !usedLabels[key]; });

return _.chain(lines)
.flatten()
.compact()
.filter((line) => { return !unusedLabels[line]; })
.value()
.join("\n");
}

replaceJump(func, collisions, ins, args, usedLabels) {
// Check if last argument is a decimal number.
const re = /(\s+)([0-9]+)(\s?)$/;
let match = args.match(re);
if (!match) {
return args;
}

// Check instruction has a jump prefix
if (_.any(jumpPrefixes, (prefix) => { return ins.startsWith(prefix); })) {
let label = `${func}_pc${match[2]}`;
if (collisions > 0) {
label += `_${collisions}`;
}
usedLabels[label + ":"] = true; // record label use for later filtering
return `${match[1]}${label}${match[3]}`;
}

return args;
}

extractLogging(stdout) {
Expand Down
11 changes: 8 additions & 3 deletions test/golang-tests.js
Expand Up @@ -33,7 +33,7 @@ chai.use(chaiAsPromised);
chai.should();

const languages = {
go: {id: 'go'}
go: { id: 'go' }
};

const compilerProps = new properties.CompilerProps(languages, properties.fakeProps({}));
Expand All @@ -59,10 +59,12 @@ function testGoAsm(basefilename) {
};

return compiler.postProcess(result).then((output) => {
const expectedOutput = utils.splitLines(fs.readFileSync(basefilename + ".output.asm").toString()).join("\n");
const expectedOutput = utils.splitLines(fs.readFileSync(basefilename + ".output.asm").toString());

utils.splitLines(output.asm).should.deep.equal(expectedOutput);

return output.should.deep.equal({
asm: expectedOutput,
asm: expectedOutput.join("\n"),
stdout: []
});
});
Expand All @@ -72,4 +74,7 @@ describe('GO asm tests', () => {
it('Handles unknown line number correctly', () => {
return testGoAsm("test/golang/bug-901");
});
it('Rewrites PC jumps to labels', () => {
return testGoAsm("test/golang/labels");
})
});
6 changes: 4 additions & 2 deletions test/golang/bug-901.output.asm
Expand Up @@ -5,8 +5,10 @@
funcdata $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
xorl AX, AX
.loc 1 4 0
jmp 7
jmp Fun_pc7
Fun_pc4:
incq AX
Fun_pc7:
cmpq AX, $10
jlt 4
jlt Fun_pc4
ret

0 comments on commit e1dc829

Please sign in to comment.