go/src/cmd/internal/obj/riscv/obj.go

190 lines
5.9 KiB
Go
Raw Normal View History

// Copyright © 2015 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.
package riscv
import (
"cmd/internal/obj"
"cmd/internal/sys"
"fmt"
)
// TODO(jsing): Populate.
var RISCV64DWARFRegisters = map[int16]int16{}
func buildop(ctxt *obj.Link) {}
func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
// TODO(jsing): Implement.
}
// setPCs sets the Pc field in all instructions reachable from p.
// It uses pc as the initial value.
func setPCs(p *obj.Prog, pc int64) {
for ; p != nil; p = p.Link {
p.Pc = pc
pc += int64(encodingForProg(p).length)
}
}
func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
return
}
text := cursym.Func.Text
if text.As != obj.ATEXT {
ctxt.Diag("preprocess: found symbol that does not start with TEXT directive")
return
}
stacksize := text.To.Offset
if stacksize == -8 {
// Historical way to mark NOFRAME.
text.From.Sym.Set(obj.AttrNoFrame, true)
stacksize = 0
}
if stacksize < 0 {
ctxt.Diag("negative frame size %d - did you mean NOFRAME?", stacksize)
}
if text.From.Sym.NoFrame() {
if stacksize != 0 {
ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", stacksize)
}
}
cursym.Func.Args = text.To.Val.(int32)
cursym.Func.Locals = int32(stacksize)
// TODO(jsing): Implement.
setPCs(cursym.Func.Text, 0)
// Validate all instructions - this provides nice error messages.
for p := cursym.Func.Text; p != nil; p = p.Link {
encodingForProg(p).validate(p)
}
}
func validateRaw(p *obj.Prog) {
// Treat the raw value specially as a 32-bit unsigned integer.
// Nobody wants to enter negative machine code.
a := p.From
if a.Type != obj.TYPE_CONST {
p.Ctxt.Diag("%v\texpected immediate in raw position but got %s", p, obj.Dconv(p, &a))
return
}
if a.Offset < 0 || 1<<32 <= a.Offset {
p.Ctxt.Diag("%v\timmediate in raw position cannot be larger than 32 bits but got %d", p, a.Offset)
}
}
func encodeRaw(p *obj.Prog) uint32 {
// Treat the raw value specially as a 32-bit unsigned integer.
// Nobody wants to enter negative machine code.
a := p.From
if a.Type != obj.TYPE_CONST {
panic(fmt.Sprintf("ill typed: %+v", a))
}
if a.Offset < 0 || 1<<32 <= a.Offset {
panic(fmt.Sprintf("immediate %d in %v cannot fit in 32 bits", a.Offset, a))
}
return uint32(a.Offset)
}
type encoding struct {
encode func(*obj.Prog) uint32 // encode returns the machine code for an *obj.Prog
validate func(*obj.Prog) // validate validates an *obj.Prog, calling ctxt.Diag for any issues
length int // length of encoded instruction; 0 for pseudo-ops, 4 otherwise
}
var (
rawEncoding = encoding{encode: encodeRaw, validate: validateRaw, length: 4}
// pseudoOpEncoding panics if encoding is attempted, but does no validation.
pseudoOpEncoding = encoding{encode: nil, validate: func(*obj.Prog) {}, length: 0}
// badEncoding is used when an invalid op is encountered.
// An error has already been generated, so let anything else through.
badEncoding = encoding{encode: func(*obj.Prog) uint32 { return 0 }, validate: func(*obj.Prog) {}, length: 0}
)
// encodingForAs contains the encoding for a RISC-V instruction.
// Instructions are masked with obj.AMask to keep indices small.
var encodingForAs = [ALAST & obj.AMask]encoding{
// TODO(jsing): Implement remaining instructions.
// Escape hatch
AWORD & obj.AMask: rawEncoding,
// Pseudo-operations
obj.AFUNCDATA: pseudoOpEncoding,
obj.APCDATA: pseudoOpEncoding,
obj.ATEXT: pseudoOpEncoding,
obj.ANOP: pseudoOpEncoding,
}
// encodingForProg returns the encoding (encode+validate funcs) for an *obj.Prog.
func encodingForProg(p *obj.Prog) encoding {
if base := p.As &^ obj.AMask; base != obj.ABaseRISCV && base != 0 {
p.Ctxt.Diag("encodingForProg: not a RISC-V instruction %s", p.As)
return badEncoding
}
as := p.As & obj.AMask
if int(as) >= len(encodingForAs) {
p.Ctxt.Diag("encodingForProg: bad RISC-V instruction %s", p.As)
return badEncoding
}
enc := encodingForAs[as]
if enc.validate == nil {
p.Ctxt.Diag("encodingForProg: no encoding for instruction %s", p.As)
return badEncoding
}
return enc
}
// assemble emits machine code.
// It is called at the very end of the assembly process.
func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
var symcode []uint32
for p := cursym.Func.Text; p != nil; p = p.Link {
enc := encodingForProg(p)
if enc.length > 0 {
symcode = append(symcode, enc.encode(p))
}
}
cursym.Size = int64(4 * len(symcode))
cursym.Grow(cursym.Size)
for p, i := cursym.P, 0; i < len(symcode); p, i = p[4:], i+1 {
ctxt.Arch.ByteOrder.PutUint32(p, symcode[i])
}
}
var LinkRISCV64 = obj.LinkArch{
Arch: sys.ArchRISCV64,
Init: buildop,
Preprocess: preprocess,
Assemble: assemble,
Progedit: progedit,
UnaryDst: unaryDst,
DWARFRegisters: RISCV64DWARFRegisters,
}