Commit b3b87143 authored by Russ Cox's avatar Russ Cox

cmd/gc: support for "portable" optimization logic

Code in gc/popt.c is compiled as part of 5g, 6g, and 8g,
meaning it can use arch-specific headers but there's
just one copy of the code.

This is the same arrangement we use for the portable
code generation logic in gc/pgen.c.

Move fixjmp and noreturn there to get the ball rolling.

R=ken2
CC=golang-dev
https://golang.org/cl/12789043
parent 4f86a96a
......@@ -39,7 +39,7 @@ struct Prog
uint32 loc; // pc offset in this func
uint32 lineno; // source line that generated this
Prog* link; // next instruction in this func
void* regp; // points to enclosing Reg struct
void* opt; // for optimizer passes
short as; // opcode
uchar reg; // doubles as width in DATA op
uchar scond;
......
......@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "../gc/popt.h"
#define Z N
#define Adr Addr
......@@ -50,7 +52,7 @@ typedef struct Rgn Rgn;
// A Reg is a wrapper around a single Prog (one instruction) that holds
// register optimization information while the optimizer runs.
// r->prog is the instruction.
// r->prog->regp points back to r.
// r->prog->opt points back to r.
struct Reg
{
......@@ -140,7 +142,6 @@ uint32 paint2(Reg*, int);
void paint3(Reg*, int, int32, int);
void addreg(Adr*, int);
void dumpit(char *str, Reg *r0);
int noreturn(Prog *p);
/*
* peep.c
......@@ -217,3 +218,10 @@ enum
};
void proginfo(ProgInfo*, Prog*);
// To allow use of AJMP and ACALL in ../gc/popt.c.
enum
{
AJMP = AB,
ACALL = ABL,
};
......@@ -69,7 +69,7 @@ peep(void)
r2->link = r1;
r2->prog = p;
p->regp = r2;
p->opt = r2;
r2->p1 = r;
r->s1 = r2;
......
......@@ -26,6 +26,8 @@ static ProgInfo progtable[ALAST] = {
[ATEXT]= {Pseudo},
[AFUNCDATA]= {Pseudo},
[APCDATA]= {Pseudo},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
......
......@@ -36,14 +36,10 @@
#define NREGVAR 32
#define REGBITS ((uint32)0xffffffff)
#define P2R(p) (Reg*)(p->reg)
void addsplits(void);
int noreturn(Prog *p);
static int first = 0;
static void fixjmp(Prog*);
Reg*
rega(void)
......@@ -260,7 +256,7 @@ regopt(Prog *firstp)
lastr = r;
}
r->prog = p;
p->regp = r;
p->opt = r;
r1 = r->p1;
if(r1 != R) {
......@@ -329,7 +325,7 @@ regopt(Prog *firstp)
if(p->to.type == D_BRANCH) {
if(p->to.u.branch == P)
fatal("pnil %P", p);
r1 = p->to.u.branch->regp;
r1 = p->to.u.branch->opt;
if(r1 == R)
fatal("rnil %P", p);
if(r1 == r) {
......@@ -1467,31 +1463,6 @@ BtoF(int32 b)
return bitno(b) - 16;
}
static Sym* symlist[10];
int
noreturn(Prog *p)
{
Sym *s;
int i;
if(symlist[0] == S) {
symlist[0] = pkglookup("panicindex", runtimepkg);
symlist[1] = pkglookup("panicslice", runtimepkg);
symlist[2] = pkglookup("throwinit", runtimepkg);
symlist[3] = pkglookup("panic", runtimepkg);
symlist[4] = pkglookup("panicwrap", runtimepkg);
}
s = p->to.sym;
if(s == S)
return 0;
for(i=0; symlist[i]!=S; i++)
if(s == symlist[i])
return 1;
return 0;
}
void
dumpone(Reg *r)
{
......@@ -1559,123 +1530,3 @@ dumpit(char *str, Reg *r0)
// }
}
}
/*
* the code generator depends on being able to write out JMP (B)
* instructions that it can jump to now but fill in later.
* the linker will resolve them nicely, but they make the code
* longer and more difficult to follow during debugging.
* remove them.
*/
/* what instruction does a JMP to p eventually land on? */
static Prog*
chasejmp(Prog *p, int *jmploop)
{
int n;
n = 0;
while(p != P && p->as == AB && p->to.type == D_BRANCH) {
if(++n > 10) {
*jmploop = 1;
break;
}
p = p->to.u.branch;
}
return p;
}
/*
* reuse reg pointer for mark/sweep state.
* leave reg==nil at end because alive==nil.
*/
#define alive ((void*)0)
#define dead ((void*)1)
/* mark all code reachable from firstp as alive */
static void
mark(Prog *firstp)
{
Prog *p;
for(p=firstp; p; p=p->link) {
if(p->regp != dead)
break;
p->regp = alive;
if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch)
mark(p->to.u.branch);
if(p->as == AB || p->as == ARET || (p->as == ABL && noreturn(p)))
break;
}
}
static void
fixjmp(Prog *firstp)
{
int jmploop;
Prog *p, *last;
if(debug['R'] && debug['v'])
print("\nfixjmp\n");
// pass 1: resolve jump to B, mark all code as dead.
jmploop = 0;
for(p=firstp; p; p=p->link) {
if(debug['R'] && debug['v'])
print("%P\n", p);
if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AB) {
p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
if(debug['R'] && debug['v'])
print("->%P\n", p);
}
p->regp = dead;
}
if(debug['R'] && debug['v'])
print("\n");
// pass 2: mark all reachable code alive
mark(firstp);
// pass 3: delete dead code (mostly JMPs).
last = nil;
for(p=firstp; p; p=p->link) {
if(p->regp == dead) {
if(p->link == P && p->as == ARET && last && last->as != ARET) {
// This is the final ARET, and the code so far doesn't have one.
// Let it stay.
} else {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
}
if(last)
last->link = p;
last = p;
}
last->link = P;
// pass 4: elide JMP to next instruction.
// only safe if there are no jumps to JMPs anymore.
if(!jmploop) {
last = nil;
for(p=firstp; p; p=p->link) {
if(p->as == AB && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
if(last)
last->link = p;
last = p;
}
last->link = P;
}
if(debug['R'] && debug['v']) {
print("\n");
for(p=firstp; p; p=p->link)
print("%P\n", p);
print("\n");
}
}
......@@ -41,7 +41,7 @@ struct Prog
Addr from; // src address
Addr to; // dst address
Prog* link; // next instruction in this func
void* reg; // pointer to containing Reg struct
void* opt; // for optimizer passes
};
#define TEXTFLAG from.scale
......
......@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "../gc/popt.h"
#define Z N
#define Adr Addr
......@@ -50,7 +52,7 @@ typedef struct Rgn Rgn;
// A Reg is a wrapper around a single Prog (one instruction) that holds
// register optimization information while the optimizer runs.
// r->prog is the instruction.
// r->prog->regp points back to r.
// r->prog->opt points back to r.
struct Reg
{
......@@ -141,7 +143,6 @@ void paint3(Reg*, int, int32, int);
void addreg(Adr*, int);
void dumpone(Reg*);
void dumpit(char*, Reg*);
int noreturn(Prog *p);
/*
* peep.c
......
......@@ -102,7 +102,7 @@ peep(void)
r2->link = r1;
r2->prog = p;
p->reg = r2;
p->opt = r2;
r2->p1 = r;
r->s1 = r2;
......
......@@ -38,12 +38,13 @@ static ProgInfo progtable[ALAST] = {
[ATEXT]= {Pseudo},
[AFUNCDATA]= {Pseudo},
[APCDATA]= {Pseudo},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
[ANOP]= {LeftRead | RightWrite},
[AADCL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry},
[AADCQ]= {SizeQ | LeftRead | RightRdwr | SetCarry | UseCarry},
[AADCW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},
......@@ -269,10 +270,6 @@ static ProgInfo progtable[ALAST] = {
[AUCOMISD]= {SizeD | LeftRead | RightRead},
[AUCOMISS]= {SizeF | LeftRead | RightRead},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
[AXCHGB]= {SizeB | LeftRdwr | RightRdwr},
[AXCHGL]= {SizeL | LeftRdwr | RightRdwr},
[AXCHGQ]= {SizeQ | LeftRdwr | RightRdwr},
......
......@@ -35,7 +35,6 @@
#define NREGVAR 32 /* 16 general + 16 floating */
#define REGBITS ((uint32)0xffffffff)
#define P2R(p) (Reg*)(p->reg)
static int first = 1;
......@@ -153,8 +152,6 @@ static char* regname[] = {
static Node* regnodes[NREGVAR];
static void fixjmp(Prog*);
void
regopt(Prog *firstp)
{
......@@ -234,7 +231,7 @@ regopt(Prog *firstp)
lastr = r;
}
r->prog = p;
p->reg = r;
p->opt = r;
r1 = r->p1;
if(r1 != R) {
......@@ -305,7 +302,7 @@ regopt(Prog *firstp)
if(p->to.type == D_BRANCH) {
if(p->to.u.branch == P)
fatal("pnil %P", p);
r1 = p->to.u.branch->reg;
r1 = p->to.u.branch->opt;
if(r1 == R)
fatal("rnil %P", p);
if(r1 == r) {
......@@ -1400,148 +1397,3 @@ dumpit(char *str, Reg *r0)
// }
}
}
static Sym* symlist[10];
int
noreturn(Prog *p)
{
Sym *s;
int i;
if(symlist[0] == S) {
symlist[0] = pkglookup("panicindex", runtimepkg);
symlist[1] = pkglookup("panicslice", runtimepkg);
symlist[2] = pkglookup("throwinit", runtimepkg);
symlist[3] = pkglookup("panic", runtimepkg);
symlist[4] = pkglookup("panicwrap", runtimepkg);
}
s = p->to.sym;
if(s == S)
return 0;
for(i=0; symlist[i]!=S; i++)
if(s == symlist[i])
return 1;
return 0;
}
/*
* the code generator depends on being able to write out JMP
* instructions that it can jump to now but fill in later.
* the linker will resolve them nicely, but they make the code
* longer and more difficult to follow during debugging.
* remove them.
*/
/* what instruction does a JMP to p eventually land on? */
static Prog*
chasejmp(Prog *p, int *jmploop)
{
int n;
n = 0;
while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
if(++n > 10) {
*jmploop = 1;
break;
}
p = p->to.u.branch;
}
return p;
}
/*
* reuse reg pointer for mark/sweep state.
* leave reg==nil at end because alive==nil.
*/
#define alive ((void*)0)
#define dead ((void*)1)
/* mark all code reachable from firstp as alive */
static void
mark(Prog *firstp)
{
Prog *p;
for(p=firstp; p; p=p->link) {
if(p->reg != dead)
break;
p->reg = alive;
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
mark(p->to.u.branch);
if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
break;
}
}
static void
fixjmp(Prog *firstp)
{
int jmploop;
Prog *p, *last;
if(debug['R'] && debug['v'])
print("\nfixjmp\n");
// pass 1: resolve jump to AJMP, mark all code as dead.
jmploop = 0;
for(p=firstp; p; p=p->link) {
if(debug['R'] && debug['v'])
print("%P\n", p);
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
if(debug['R'] && debug['v'])
print("->%P\n", p);
}
p->reg = dead;
}
if(debug['R'] && debug['v'])
print("\n");
// pass 2: mark all reachable code alive
mark(firstp);
// pass 3: delete dead code (mostly JMPs).
last = nil;
for(p=firstp; p; p=p->link) {
if(p->reg == dead) {
if(p->link == P && p->as == ARET && last && last->as != ARET) {
// This is the final ARET, and the code so far doesn't have one.
// Let it stay.
} else {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
}
if(last)
last->link = p;
last = p;
}
last->link = P;
// pass 4: elide JMP to next instruction.
// only safe if there are no jumps to JMPs anymore.
if(!jmploop) {
last = nil;
for(p=firstp; p; p=p->link) {
if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
if(last)
last->link = p;
last = p;
}
last->link = P;
}
if(debug['R'] && debug['v']) {
print("\n");
for(p=firstp; p; p=p->link)
print("%P\n", p);
print("\n");
}
}
......@@ -42,7 +42,7 @@ struct Prog
Addr from; // src address
Addr to; // dst address
Prog* link; // next instruction in this func
void* reg; // pointer to containing Reg struct
void* opt; // for optimizer passes
};
#define TEXTFLAG from.scale
......
......@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "../gc/popt.h"
#define Z N
#define Adr Addr
......@@ -50,7 +52,7 @@ typedef struct Rgn Rgn;
// A Reg is a wrapper around a single Prog (one instruction) that holds
// register optimization information while the optimizer runs.
// r->prog is the instruction.
// r->prog->regp points back to r.
// r->prog->opt points back to r.
struct Reg
{
......@@ -141,7 +143,6 @@ void paint3(Reg*, int, int32, int);
void addreg(Adr*, int);
void dumpone(Reg*);
void dumpit(char*, Reg*);
int noreturn(Prog *p);
/*
* peep.c
......
......@@ -99,7 +99,7 @@ peep(void)
r2->link = r1;
r2->prog = p;
p->reg = r2;
p->opt = r2;
r2->p1 = r;
r->s1 = r2;
......
......@@ -38,6 +38,8 @@ static ProgInfo progtable[ALAST] = {
[ATEXT]= {Pseudo},
[AFUNCDATA]= {Pseudo},
[APCDATA]= {Pseudo},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
......@@ -287,10 +289,6 @@ static ProgInfo progtable[ALAST] = {
[AUCOMISD]= {SizeD | LeftRead | RightRead},
[AUCOMISS]= {SizeF | LeftRead | RightRead},
[AUNDEF]= {OK},
[AUSEFIELD]= {OK},
[AXCHGB]= {SizeB | LeftRdwr | RightRdwr},
[AXCHGL]= {SizeL | LeftRdwr | RightRdwr},
[AXCHGW]= {SizeW | LeftRdwr | RightRdwr},
......
......@@ -35,11 +35,9 @@
#define NREGVAR 16 /* 8 integer + 8 floating */
#define REGBITS ((uint32)0xffff)
#define P2R(p) (Reg*)(p->reg)
static int first = 1;
static void fixjmp(Prog*);
static void fixtemp(Prog*);
Reg*
......@@ -206,7 +204,7 @@ regopt(Prog *firstp)
lastr = r;
}
r->prog = p;
p->reg = r;
p->opt = r;
r1 = r->p1;
if(r1 != R) {
......@@ -277,7 +275,7 @@ regopt(Prog *firstp)
if(p->to.type == D_BRANCH) {
if(p->to.u.branch == P)
fatal("pnil %P", p);
r1 = p->to.u.branch->reg;
r1 = p->to.u.branch->opt;
if(r1 == R)
fatal("rnil %P", p);
if(r1 == r) {
......@@ -1366,151 +1364,6 @@ dumpit(char *str, Reg *r0)
}
}
static Sym* symlist[10];
int
noreturn(Prog *p)
{
Sym *s;
int i;
if(symlist[0] == S) {
symlist[0] = pkglookup("panicindex", runtimepkg);
symlist[1] = pkglookup("panicslice", runtimepkg);
symlist[2] = pkglookup("throwinit", runtimepkg);
symlist[3] = pkglookup("panic", runtimepkg);
symlist[4] = pkglookup("panicwrap", runtimepkg);
}
s = p->to.sym;
if(s == S)
return 0;
for(i=0; symlist[i]!=S; i++)
if(s == symlist[i])
return 1;
return 0;
}
/*
* the code generator depends on being able to write out JMP
* instructions that it can jump to now but fill in later.
* the linker will resolve them nicely, but they make the code
* longer and more difficult to follow during debugging.
* remove them.
*/
/* what instruction does a JMP to p eventually land on? */
static Prog*
chasejmp(Prog *p, int *jmploop)
{
int n;
n = 0;
while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
if(++n > 10) {
*jmploop = 1;
break;
}
p = p->to.u.branch;
}
return p;
}
/*
* reuse reg pointer for mark/sweep state.
* leave reg==nil at end because alive==nil.
*/
#define alive ((void*)0)
#define dead ((void*)1)
/* mark all code reachable from firstp as alive */
static void
mark(Prog *firstp)
{
Prog *p;
for(p=firstp; p; p=p->link) {
if(p->reg != dead)
break;
p->reg = alive;
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
mark(p->to.u.branch);
if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
break;
}
}
static void
fixjmp(Prog *firstp)
{
int jmploop;
Prog *p, *last;
if(debug['R'] && debug['v'])
print("\nfixjmp\n");
// pass 1: resolve jump to AJMP, mark all code as dead.
jmploop = 0;
for(p=firstp; p; p=p->link) {
if(debug['R'] && debug['v'])
print("%P\n", p);
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
if(debug['R'] && debug['v'])
print("->%P\n", p);
}
p->reg = dead;
}
if(debug['R'] && debug['v'])
print("\n");
// pass 2: mark all reachable code alive
mark(firstp);
// pass 3: delete dead code (mostly JMPs).
last = nil;
for(p=firstp; p; p=p->link) {
if(p->reg == dead) {
if(p->link == P && p->as == ARET && last && last->as != ARET) {
// This is the final ARET, and the code so far doesn't have one.
// Let it stay.
} else {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
}
if(last)
last->link = p;
last = p;
}
last->link = P;
// pass 4: elide JMP to next instruction.
// only safe if there are no jumps to JMPs anymore.
if(!jmploop) {
last = nil;
for(p=firstp; p; p=p->link) {
if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
if(last)
last->link = p;
last = p;
}
last->link = P;
}
if(debug['R'] && debug['v']) {
print("\n");
for(p=firstp; p; p=p->link)
print("%P\n", p);
print("\n");
}
}
static uint32
fnv1(Sym *sym)
{
......
......@@ -489,6 +489,7 @@ static struct {
{"cmd/gc", {
"-cplx.c",
"-pgen.c",
"-popt.c",
"-y1.tab.c", // makefile dreg
"opnames.h",
}},
......@@ -513,18 +514,24 @@ static struct {
{"cmd/5g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../gc/popt.c",
"../gc/popt.h",
"../5l/enam.c",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
}},
{"cmd/6g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../gc/popt.c",
"../gc/popt.h",
"../6l/enam.c",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
}},
{"cmd/8g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../gc/popt.c",
"../gc/popt.h",
"../8l/enam.c",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
}},
......
......@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// "Portable" code generation.
// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
// Must code to the intersection of the three back ends.
#include <u.h>
#include <libc.h>
#include "gg.h"
......
// Derived from Inferno utils/6c/reg.c
// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// "Portable" optimizations.
// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
// Must code to the intersection of the three back ends.
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
// p is a call instruction. Does the call fail to return?
int
noreturn(Prog *p)
{
Sym *s;
int i;
static Sym* symlist[10];
if(symlist[0] == S) {
symlist[0] = pkglookup("panicindex", runtimepkg);
symlist[1] = pkglookup("panicslice", runtimepkg);
symlist[2] = pkglookup("throwinit", runtimepkg);
symlist[3] = pkglookup("panic", runtimepkg);
symlist[4] = pkglookup("panicwrap", runtimepkg);
}
s = p->to.sym;
if(s == S)
return 0;
for(i=0; symlist[i]!=S; i++)
if(s == symlist[i])
return 1;
return 0;
}
// JMP chasing and removal.
//
// The code generator depends on being able to write out jump
// instructions that it can jump to now but fill in later.
// the linker will resolve them nicely, but they make the code
// longer and more difficult to follow during debugging.
// Remove them.
/* what instruction does a JMP to p eventually land on? */
static Prog*
chasejmp(Prog *p, int *jmploop)
{
int n;
n = 0;
while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
if(++n > 10) {
*jmploop = 1;
break;
}
p = p->to.u.branch;
}
return p;
}
/*
* reuse reg pointer for mark/sweep state.
* leave reg==nil at end because alive==nil.
*/
#define alive ((void*)0)
#define dead ((void*)1)
/* mark all code reachable from firstp as alive */
static void
mark(Prog *firstp)
{
Prog *p;
for(p=firstp; p; p=p->link) {
if(p->opt != dead)
break;
p->opt = alive;
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
mark(p->to.u.branch);
if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
break;
}
}
void
fixjmp(Prog *firstp)
{
int jmploop;
Prog *p, *last;
if(debug['R'] && debug['v'])
print("\nfixjmp\n");
// pass 1: resolve jump to jump, mark all code as dead.
jmploop = 0;
for(p=firstp; p; p=p->link) {
if(debug['R'] && debug['v'])
print("%P\n", p);
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
if(debug['R'] && debug['v'])
print("->%P\n", p);
}
p->opt = dead;
}
if(debug['R'] && debug['v'])
print("\n");
// pass 2: mark all reachable code alive
mark(firstp);
// pass 3: delete dead code (mostly JMPs).
last = nil;
for(p=firstp; p; p=p->link) {
if(p->opt == dead) {
if(p->link == P && p->as == ARET && last && last->as != ARET) {
// This is the final ARET, and the code so far doesn't have one.
// Let it stay.
} else {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
}
if(last)
last->link = p;
last = p;
}
last->link = P;
// pass 4: elide JMP to next instruction.
// only safe if there are no jumps to JMPs anymore.
if(!jmploop) {
last = nil;
for(p=firstp; p; p=p->link) {
if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
if(last)
last->link = p;
last = p;
}
last->link = P;
}
if(debug['R'] && debug['v']) {
print("\n");
for(p=firstp; p; p=p->link)
print("%P\n", p);
print("\n");
}
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
void fixjmp(Prog*);
int noreturn(Prog*);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment