Merge pull request #688 from nspcc-dev/fix/jmplabels
compiler: use uint16 for labels
This commit is contained in:
commit
71a18b1727
3 changed files with 49 additions and 35 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -39,7 +40,7 @@ type codegen struct {
|
||||||
scope *funcScope
|
scope *funcScope
|
||||||
|
|
||||||
// A mapping from label's names to their ids.
|
// A mapping from label's names to their ids.
|
||||||
labels map[labelWithType]int
|
labels map[labelWithType]uint16
|
||||||
|
|
||||||
// A label for the for-loop being currently visited.
|
// A label for the for-loop being currently visited.
|
||||||
currentFor string
|
currentFor string
|
||||||
|
@ -66,21 +67,26 @@ type labelWithType struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newLabel creates a new label to jump to
|
// newLabel creates a new label to jump to
|
||||||
func (c *codegen) newLabel() (l int) {
|
func (c *codegen) newLabel() (l uint16) {
|
||||||
l = len(c.l)
|
li := len(c.l)
|
||||||
|
if li > math.MaxUint16 {
|
||||||
|
c.prog.Err = errors.New("label number is too big")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l = uint16(li)
|
||||||
c.l = append(c.l, -1)
|
c.l = append(c.l, -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNamedLabel creates a new label with a specified name.
|
// newNamedLabel creates a new label with a specified name.
|
||||||
func (c *codegen) newNamedLabel(typ labelOffsetType, name string) (l int) {
|
func (c *codegen) newNamedLabel(typ labelOffsetType, name string) (l uint16) {
|
||||||
l = c.newLabel()
|
l = c.newLabel()
|
||||||
lt := labelWithType{name: name, typ: typ}
|
lt := labelWithType{name: name, typ: typ}
|
||||||
c.labels[lt] = l
|
c.labels[lt] = l
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) setLabel(l int) {
|
func (c *codegen) setLabel(l uint16) {
|
||||||
c.l[l] = c.pc() + 1
|
c.l[l] = c.pc() + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,13 +390,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
|
|
||||||
if n.Cond != nil {
|
if n.Cond != nil {
|
||||||
ast.Walk(c, n.Cond)
|
ast.Walk(c, n.Cond)
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(lElse))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, lElse)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setLabel(lIf)
|
c.setLabel(lIf)
|
||||||
ast.Walk(c, n.Body)
|
ast.Walk(c, n.Body)
|
||||||
if n.Else != nil {
|
if n.Else != nil {
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(lElseEnd))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, lElseEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setLabel(lElse)
|
c.setLabel(lElse)
|
||||||
|
@ -421,9 +427,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
ast.Walk(c, cc.List[j])
|
ast.Walk(c, cc.List[j])
|
||||||
emit.Opcode(c.prog.BinWriter, eqOpcode)
|
emit.Opcode(c.prog.BinWriter, eqOpcode)
|
||||||
if j == l-1 {
|
if j == l-1 {
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(lEnd))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, lEnd)
|
||||||
} else {
|
} else {
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, int16(lStart))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, lStart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,7 +438,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
for _, stmt := range cc.Body {
|
for _, stmt := range cc.Body {
|
||||||
ast.Walk(c, stmt)
|
ast.Walk(c, stmt)
|
||||||
}
|
}
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(switchEnd))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, switchEnd)
|
||||||
c.setLabel(lEnd)
|
c.setLabel(lEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,13 +506,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
switch n.Op {
|
switch n.Op {
|
||||||
case token.LAND:
|
case token.LAND:
|
||||||
ast.Walk(c, n.X)
|
ast.Walk(c, n.X)
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(len(c.l)-1))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, uint16(len(c.l)-1))
|
||||||
ast.Walk(c, n.Y)
|
ast.Walk(c, n.Y)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case token.LOR:
|
case token.LOR:
|
||||||
ast.Walk(c, n.X)
|
ast.Walk(c, n.X)
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, int16(len(c.l)-3))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, uint16(len(c.l)-3))
|
||||||
ast.Walk(c, n.Y)
|
ast.Walk(c, n.Y)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -612,7 +618,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
case isSyscall(f):
|
case isSyscall(f):
|
||||||
c.convertSyscall(f.selector.Name, f.name)
|
c.convertSyscall(f.selector.Name, f.name)
|
||||||
default:
|
default:
|
||||||
emit.Call(c.prog.BinWriter, opcode.CALL, int16(f.label))
|
emit.Call(c.prog.BinWriter, opcode.CALL, f.label)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -702,10 +708,10 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
switch n.Tok {
|
switch n.Tok {
|
||||||
case token.BREAK:
|
case token.BREAK:
|
||||||
end := c.getLabelOffset(labelEnd, label)
|
end := c.getLabelOffset(labelEnd, label)
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(end))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, end)
|
||||||
case token.CONTINUE:
|
case token.CONTINUE:
|
||||||
post := c.getLabelOffset(labelPost, label)
|
post := c.getLabelOffset(labelPost, label)
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(post))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, post)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -737,7 +743,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
ast.Walk(c, n.Cond)
|
ast.Walk(c, n.Cond)
|
||||||
|
|
||||||
// Jump if the condition is false
|
// Jump if the condition is false
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(fend))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, fend)
|
||||||
|
|
||||||
// Walk body followed by the iterator (post stmt).
|
// Walk body followed by the iterator (post stmt).
|
||||||
ast.Walk(c, n.Body)
|
ast.Walk(c, n.Body)
|
||||||
|
@ -747,7 +753,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump back to condition.
|
// Jump back to condition.
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(fstart))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, fstart)
|
||||||
c.setLabel(fend)
|
c.setLabel(fend)
|
||||||
|
|
||||||
c.currentFor = lastLabel
|
c.currentFor = lastLabel
|
||||||
|
@ -782,7 +788,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.OVER)
|
emit.Opcode(c.prog.BinWriter, opcode.OVER)
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.OVER)
|
emit.Opcode(c.prog.BinWriter, opcode.OVER)
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.LTE) // finish if len <= i
|
emit.Opcode(c.prog.BinWriter, opcode.LTE) // finish if len <= i
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, int16(end))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, end)
|
||||||
|
|
||||||
if n.Key != nil {
|
if n.Key != nil {
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||||
|
@ -796,7 +802,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
c.setLabel(post)
|
c.setLabel(post)
|
||||||
|
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.INC)
|
emit.Opcode(c.prog.BinWriter, opcode.INC)
|
||||||
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(start))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, start)
|
||||||
|
|
||||||
c.setLabel(end)
|
c.setLabel(end)
|
||||||
|
|
||||||
|
@ -833,7 +839,7 @@ func (c *codegen) emitReverse(num int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateLabel returns a new label.
|
// generateLabel returns a new label.
|
||||||
func (c *codegen) generateLabel(typ labelOffsetType) (int, string) {
|
func (c *codegen) generateLabel(typ labelOffsetType) (uint16, string) {
|
||||||
name := c.nextLabel
|
name := c.nextLabel
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = fmt.Sprintf("@%d", len(c.l))
|
name = fmt.Sprintf("@%d", len(c.l))
|
||||||
|
@ -843,7 +849,7 @@ func (c *codegen) generateLabel(typ labelOffsetType) (int, string) {
|
||||||
return c.newNamedLabel(typ, name), name
|
return c.newNamedLabel(typ, name), name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) getLabelOffset(typ labelOffsetType, name string) int {
|
func (c *codegen) getLabelOffset(typ labelOffsetType, name string) uint16 {
|
||||||
return c.labels[labelWithType{name: name, typ: typ}]
|
return c.labels[labelWithType{name: name, typ: typ}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1144,7 +1150,7 @@ func CodeGen(info *buildInfo) ([]byte, error) {
|
||||||
prog: io.NewBufBinWriter(),
|
prog: io.NewBufBinWriter(),
|
||||||
l: []int{},
|
l: []int{},
|
||||||
funcs: map[string]*funcScope{},
|
funcs: map[string]*funcScope{},
|
||||||
labels: map[labelWithType]int{},
|
labels: map[labelWithType]uint16{},
|
||||||
typeInfo: &pkg.Info,
|
typeInfo: &pkg.Info,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1197,7 +1203,9 @@ func CodeGen(info *buildInfo) ([]byte, error) {
|
||||||
return nil, c.prog.Err
|
return nil, c.prog.Err
|
||||||
}
|
}
|
||||||
buf := c.prog.Bytes()
|
buf := c.prog.Bytes()
|
||||||
c.writeJumps(buf)
|
if err := c.writeJumps(buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,20 +1220,26 @@ func (c *codegen) resolveFuncDecls(f *ast.File) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) writeJumps(b []byte) {
|
func (c *codegen) writeJumps(b []byte) error {
|
||||||
ctx := vm.NewContext(b)
|
ctx := vm.NewContext(b)
|
||||||
for op, _, err := ctx.Next(); err == nil && ctx.NextIP() < len(b); op, _, err = ctx.Next() {
|
for op, _, err := ctx.Next(); err == nil && ctx.NextIP() < len(b); op, _, err = ctx.Next() {
|
||||||
switch op {
|
switch op {
|
||||||
case opcode.JMP, opcode.JMPIFNOT, opcode.JMPIF, opcode.CALL:
|
case opcode.JMP, opcode.JMPIFNOT, opcode.JMPIF, opcode.CALL:
|
||||||
// we can't use arg returned by ctx.Next() because it is copied
|
// we can't use arg returned by ctx.Next() because it is copied
|
||||||
arg := b[ctx.NextIP()-2:]
|
nextIP := ctx.NextIP()
|
||||||
|
arg := b[nextIP-2:]
|
||||||
|
|
||||||
index := int16(binary.LittleEndian.Uint16(arg))
|
index := binary.LittleEndian.Uint16(arg)
|
||||||
if int(index) > len(c.l) || int(index) < 0 {
|
if int(index) > len(c.l) {
|
||||||
continue
|
return fmt.Errorf("unexpected label number: %d (max %d)", index, len(c.l))
|
||||||
}
|
}
|
||||||
offset := uint16(c.l[index] - ctx.NextIP() + 3)
|
offset := c.l[index] - nextIP + 3
|
||||||
binary.LittleEndian.PutUint16(arg, offset)
|
if offset > math.MaxUint16 {
|
||||||
|
return fmt.Errorf("label offset is too big at the instruction %d: %d (max %d)",
|
||||||
|
nextIP-3, offset, math.MaxUint16)
|
||||||
|
}
|
||||||
|
binary.LittleEndian.PutUint16(arg, uint16(offset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ type funcScope struct {
|
||||||
decl *ast.FuncDecl
|
decl *ast.FuncDecl
|
||||||
|
|
||||||
// Program label of the scope
|
// Program label of the scope
|
||||||
label int
|
label uint16
|
||||||
|
|
||||||
// Local variables
|
// Local variables
|
||||||
locals map[string]int
|
locals map[string]int
|
||||||
|
@ -35,7 +35,7 @@ type funcScope struct {
|
||||||
i int
|
i int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFuncScope(decl *ast.FuncDecl, label int) *funcScope {
|
func newFuncScope(decl *ast.FuncDecl, label uint16) *funcScope {
|
||||||
return &funcScope{
|
return &funcScope{
|
||||||
name: decl.Name.Name,
|
name: decl.Name.Name,
|
||||||
decl: decl,
|
decl: decl,
|
||||||
|
|
|
@ -91,12 +91,12 @@ func Syscall(w *io.BinWriter, api string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call emits a call Instruction with label to the given buffer.
|
// Call emits a call Instruction with label to the given buffer.
|
||||||
func Call(w *io.BinWriter, op opcode.Opcode, label int16) {
|
func Call(w *io.BinWriter, op opcode.Opcode, label uint16) {
|
||||||
Jmp(w, op, label)
|
Jmp(w, op, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jmp emits a jump Instruction along with label to the given buffer.
|
// Jmp emits a jump Instruction along with label to the given buffer.
|
||||||
func Jmp(w *io.BinWriter, op opcode.Opcode, label int16) {
|
func Jmp(w *io.BinWriter, op opcode.Opcode, label uint16) {
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
return
|
return
|
||||||
} else if !isInstructionJmp(op) {
|
} else if !isInstructionJmp(op) {
|
||||||
|
@ -104,7 +104,7 @@ func Jmp(w *io.BinWriter, op opcode.Opcode, label int16) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buf := make([]byte, 2)
|
buf := make([]byte, 2)
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(label))
|
binary.LittleEndian.PutUint16(buf, label)
|
||||||
Instruction(w, op, buf)
|
Instruction(w, op, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue