Skip to content

Commit 52b2220

Browse files
committed
cmd/asm: factor out line parsing from assembling
Currently cmd/asm's Parser.line both consumes a line of assembly from the lexer and assembles it. This CL separates these two steps so that the line parser can be reused for purposes other than generating a Prog stream. For #27539. Updates #17544. Change-Id: I452c9a2112fbcc1c94bf909efc0d1fcc71014812 Reviewed-on: https://go-review.googlesource.com/c/147097 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent 30cc978 commit 52b2220

File tree

2 files changed

+44
-24
lines changed

2 files changed

+44
-24
lines changed

src/cmd/asm/internal/asm/line_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ func testBadInstParser(t *testing.T, goarch string, tests []badInstTest) {
3838
parser := NewParser(ctxt, arch, tokenizer)
3939

4040
err := tryParse(t, func() {
41-
parser.start(lex.Tokenize(test.input))
42-
parser.line()
41+
parser.Parse()
4342
})
4443

4544
switch {

src/cmd/asm/internal/asm/parse.go

+43-22
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,23 @@ func (p *Parser) pos() src.XPos {
9191
}
9292

9393
func (p *Parser) Parse() (*obj.Prog, bool) {
94-
for p.line() {
94+
scratch := make([][]lex.Token, 0, 3)
95+
for {
96+
word, cond, operands, ok := p.line(scratch)
97+
if !ok {
98+
break
99+
}
100+
scratch = operands
101+
102+
if p.pseudo(word, operands) {
103+
continue
104+
}
105+
i, present := p.arch.Instructions[word]
106+
if present {
107+
p.instruction(i, word, cond, operands)
108+
continue
109+
}
110+
p.errorf("unrecognized instruction %q", word)
95111
}
96112
if p.errorCount > 0 {
97113
return nil, false
@@ -100,8 +116,17 @@ func (p *Parser) Parse() (*obj.Prog, bool) {
100116
return p.firstProg, true
101117
}
102118

103-
// WORD [ arg {, arg} ] (';' | '\n')
104-
func (p *Parser) line() bool {
119+
// line consumes a single assembly line from p.lex of the form
120+
//
121+
// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n')
122+
//
123+
// It adds any labels to p.pendingLabels and returns the word, cond,
124+
// operand list, and true. If there is an error or EOF, it returns
125+
// ok=false.
126+
//
127+
// line may reuse the memory from scratch.
128+
func (p *Parser) line(scratch [][]lex.Token) (word, cond string, operands [][]lex.Token, ok bool) {
129+
next:
105130
// Skip newlines.
106131
var tok lex.ScanToken
107132
for {
@@ -114,24 +139,29 @@ func (p *Parser) line() bool {
114139
case '\n', ';':
115140
continue
116141
case scanner.EOF:
117-
return false
142+
return "", "", nil, false
118143
}
119144
break
120145
}
121146
// First item must be an identifier.
122147
if tok != scanner.Ident {
123148
p.errorf("expected identifier, found %q", p.lex.Text())
124-
return false // Might as well stop now.
149+
return "", "", nil, false // Might as well stop now.
125150
}
126-
word := p.lex.Text()
127-
var cond string
128-
operands := make([][]lex.Token, 0, 3)
151+
word, cond = p.lex.Text(), ""
152+
operands = scratch[:0]
129153
// Zero or more comma-separated operands, one per loop.
130154
nesting := 0
131155
colon := -1
132156
for tok != '\n' && tok != ';' {
133157
// Process one operand.
134-
items := make([]lex.Token, 0, 3)
158+
var items []lex.Token
159+
if cap(operands) > len(operands) {
160+
// Reuse scratch items slice.
161+
items = operands[:cap(operands)][len(operands)][:0]
162+
} else {
163+
items = make([]lex.Token, 0, 3)
164+
}
135165
for {
136166
tok = p.lex.Next()
137167
if len(operands) == 0 && len(items) == 0 {
@@ -148,12 +178,12 @@ func (p *Parser) line() bool {
148178
if tok == ':' {
149179
// Labels.
150180
p.pendingLabels = append(p.pendingLabels, word)
151-
return true
181+
goto next
152182
}
153183
}
154184
if tok == scanner.EOF {
155185
p.errorf("unexpected EOF")
156-
return false
186+
return "", "", nil, false
157187
}
158188
// Split operands on comma. Also, the old syntax on x86 for a "register pair"
159189
// was AX:DX, for which the new syntax is DX, AX. Note the reordering.
@@ -162,7 +192,7 @@ func (p *Parser) line() bool {
162192
// Remember this location so we can swap the operands below.
163193
if colon >= 0 {
164194
p.errorf("invalid ':' in operand")
165-
return true
195+
return word, cond, operands, true
166196
}
167197
colon = len(operands)
168198
}
@@ -188,16 +218,7 @@ func (p *Parser) line() bool {
188218
p.errorf("missing operand")
189219
}
190220
}
191-
if p.pseudo(word, operands) {
192-
return true
193-
}
194-
i, present := p.arch.Instructions[word]
195-
if present {
196-
p.instruction(i, word, cond, operands)
197-
return true
198-
}
199-
p.errorf("unrecognized instruction %q", word)
200-
return true
221+
return word, cond, operands, true
201222
}
202223

203224
func (p *Parser) instruction(op obj.As, word, cond string, operands [][]lex.Token) {

0 commit comments

Comments
 (0)