Skip to content
Snippets Groups Projects
asm.go 53.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Copyright 2022 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.
    
    package loong64
    
    import (
    	"cmd/internal/obj"
    	"cmd/internal/objabi"
    	"cmd/internal/sys"
    	"fmt"
    	"log"
    	"sort"
    )
    
    // ctxt0 holds state while assembling a single function.
    // Each function gets a fresh ctxt0.
    // This allows for multiple functions to be safely concurrently assembled.
    type ctxt0 struct {
    	ctxt       *obj.Link
    	newprog    obj.ProgAlloc
    	cursym     *obj.LSym
    	autosize   int32
    	instoffset int64
    	pc         int64
    }
    
    // Instruction layout.
    
    const (
    	FuncAlign = 4
    )
    
    type Optab struct {
    	as     obj.As
    
    	a1     uint8 // first source operand
    	a2     uint8 // 2nd source operand
    	a3     uint8 // first destination operand
    	a4     uint8 // 2nd destination operand
    
    	type_  int8
    	size   int8
    	param  int16
    	family sys.ArchFamily
    	flag   uint8
    }
    
    const (
    	NOTUSETMP = 1 << iota // p expands to multiple instructions, but does NOT use REGTMP
    )
    
    var optab = []Optab{
    
    	{obj.ATEXT, C_ADDR, C_NONE, C_TEXTSIZE, C_NONE, 0, 0, 0, 0, 0},
    
    	{AMOVW, C_REG, C_NONE, C_REG, C_NONE, 1, 4, 0, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_REG, C_NONE, 1, 4, 0, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_REG, C_NONE, 12, 8, 0, 0, NOTUSETMP},
    	{AMOVBU, C_REG, C_NONE, C_REG, C_NONE, 13, 4, 0, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_REG, C_NONE, 14, 8, 0, sys.Loong64, NOTUSETMP},
    
    	{ASUB, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{ASUBV, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, sys.Loong64, 0},
    	{AADD, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{AADDV, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, sys.Loong64, 0},
    	{AAND, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{ASUB, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{ASUBV, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, sys.Loong64, 0},
    	{AADD, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{AADDV, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, sys.Loong64, 0},
    	{AAND, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{ANEGW, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{ANEGV, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, sys.Loong64, 0},
    	{AMASKEQZ, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, 0, 0},
    
    	{ASLL, C_REG, C_NONE, C_REG, C_NONE, 9, 4, 0, 0, 0},
    	{ASLL, C_REG, C_REG, C_REG, C_NONE, 9, 4, 0, 0, 0},
    	{ASLLV, C_REG, C_NONE, C_REG, C_NONE, 9, 4, 0, sys.Loong64, 0},
    	{ASLLV, C_REG, C_REG, C_REG, C_NONE, 9, 4, 0, sys.Loong64, 0},
    	{ACLO, C_REG, C_NONE, C_REG, C_NONE, 9, 4, 0, 0, 0},
    
    	{AADDF, C_FREG, C_NONE, C_FREG, C_NONE, 32, 4, 0, 0, 0},
    	{AADDF, C_FREG, C_REG, C_FREG, C_NONE, 32, 4, 0, 0, 0},
    	{ACMPEQF, C_FREG, C_REG, C_NONE, C_NONE, 32, 4, 0, 0, 0},
    	{AABSF, C_FREG, C_NONE, C_FREG, C_NONE, 33, 4, 0, 0, 0},
    	{AMOVVF, C_FREG, C_NONE, C_FREG, C_NONE, 33, 4, 0, sys.Loong64, 0},
    	{AMOVF, C_FREG, C_NONE, C_FREG, C_NONE, 33, 4, 0, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_FREG, C_NONE, 33, 4, 0, 0, 0},
    
    	{AMOVW, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0},
    	{AMOVWU, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0},
    	{AMOVBU, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0},
    	{AMOVWL, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0},
    	{AMOVVL, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0},
    	{AMOVW, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0, 0},
    	{AMOVWL, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0, 0},
    	{AMOVVL, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, sys.Loong64, 0},
    	{AMOVW, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0, 0},
    	{AMOVWL, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0, 0},
    	{AMOVVL, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, sys.Loong64, 0},
    	{ASC, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0, 0},
    	{ASCV, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, sys.Loong64, 0},
    
    	{AMOVW, C_SEXT, C_NONE, C_REG, C_NONE, 8, 4, 0, sys.Loong64, 0},
    	{AMOVWU, C_SEXT, C_NONE, C_REG, C_NONE, 8, 4, 0, sys.Loong64, 0},
    	{AMOVV, C_SEXT, C_NONE, C_REG, C_NONE, 8, 4, 0, sys.Loong64, 0},
    	{AMOVB, C_SEXT, C_NONE, C_REG, C_NONE, 8, 4, 0, sys.Loong64, 0},
    	{AMOVBU, C_SEXT, C_NONE, C_REG, C_NONE, 8, 4, 0, sys.Loong64, 0},
    	{AMOVWL, C_SEXT, C_NONE, C_REG, C_NONE, 8, 4, 0, sys.Loong64, 0},
    	{AMOVVL, C_SEXT, C_NONE, C_REG, C_NONE, 8, 4, 0, sys.Loong64, 0},
    	{AMOVW, C_SAUTO, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0, 0},
    	{AMOVWU, C_SAUTO, C_NONE, C_REG, C_NONE, 8, 4, REGSP, sys.Loong64, 0},
    	{AMOVV, C_SAUTO, C_NONE, C_REG, C_NONE, 8, 4, REGSP, sys.Loong64, 0},
    	{AMOVB, C_SAUTO, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0, 0},
    	{AMOVBU, C_SAUTO, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0, 0},
    	{AMOVWL, C_SAUTO, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0, 0},
    	{AMOVVL, C_SAUTO, C_NONE, C_REG, C_NONE, 8, 4, REGSP, sys.Loong64, 0},
    	{AMOVW, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0, 0},
    	{AMOVWU, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, sys.Loong64, 0},
    	{AMOVV, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, sys.Loong64, 0},
    	{AMOVB, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0, 0},
    	{AMOVBU, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0, 0},
    	{AMOVWL, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0, 0},
    	{AMOVVL, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, sys.Loong64, 0},
    	{ALL, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0, 0},
    	{ALLV, C_SOREG, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, sys.Loong64, 0},
    
    	{AMOVW, C_REG, C_NONE, C_LEXT, C_NONE, 35, 12, 0, sys.Loong64, 0},
    	{AMOVWU, C_REG, C_NONE, C_LEXT, C_NONE, 35, 12, 0, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_LEXT, C_NONE, 35, 12, 0, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_LEXT, C_NONE, 35, 12, 0, sys.Loong64, 0},
    	{AMOVBU, C_REG, C_NONE, C_LEXT, C_NONE, 35, 12, 0, sys.Loong64, 0},
    	{AMOVW, C_REG, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0, 0},
    	{AMOVW, C_REG, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0, 0},
    	{ASC, C_REG, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0, 0},
    	{AMOVW, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0, 0},
    	{AMOVW, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, sys.Loong64, 0},
    	{AMOVWU, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0, 0},
    	{AMOVB, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, sys.Loong64, 0},
    	{AMOVBU, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, sys.Loong64, 0},
    	{AMOVW, C_REG, C_NONE, C_TLS, C_NONE, 53, 16, 0, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_TLS, C_NONE, 53, 16, 0, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_TLS, C_NONE, 53, 16, 0, sys.Loong64, 0},
    	{AMOVB, C_REG, C_NONE, C_TLS, C_NONE, 53, 16, 0, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_TLS, C_NONE, 53, 16, 0, 0, 0},
    
    	{AMOVW, C_LEXT, C_NONE, C_REG, C_NONE, 36, 12, 0, sys.Loong64, 0},
    	{AMOVWU, C_LEXT, C_NONE, C_REG, C_NONE, 36, 12, 0, sys.Loong64, 0},
    	{AMOVV, C_LEXT, C_NONE, C_REG, C_NONE, 36, 12, 0, sys.Loong64, 0},
    	{AMOVB, C_LEXT, C_NONE, C_REG, C_NONE, 36, 12, 0, sys.Loong64, 0},
    	{AMOVBU, C_LEXT, C_NONE, C_REG, C_NONE, 36, 12, 0, sys.Loong64, 0},
    	{AMOVW, C_LAUTO, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0, 0},
    	{AMOVWU, C_LAUTO, C_NONE, C_REG, C_NONE, 36, 12, REGSP, sys.Loong64, 0},
    	{AMOVV, C_LAUTO, C_NONE, C_REG, C_NONE, 36, 12, REGSP, sys.Loong64, 0},
    	{AMOVB, C_LAUTO, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0, 0},
    	{AMOVBU, C_LAUTO, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0, 0},
    	{AMOVW, C_LOREG, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0, 0},
    	{AMOVWU, C_LOREG, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, sys.Loong64, 0},
    	{AMOVV, C_LOREG, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, sys.Loong64, 0},
    	{AMOVB, C_LOREG, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0, 0},
    	{AMOVBU, C_LOREG, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0, 0},
    	{AMOVW, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, 0, 0},
    	{AMOVW, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, sys.Loong64, 0},
    	{AMOVWU, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, sys.Loong64, 0},
    	{AMOVV, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, sys.Loong64, 0},
    	{AMOVB, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, 0, 0},
    	{AMOVB, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, sys.Loong64, 0},
    	{AMOVBU, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, 0, 0},
    	{AMOVBU, C_ADDR, C_NONE, C_REG, C_NONE, 51, 8, 0, sys.Loong64, 0},
    	{AMOVW, C_TLS, C_NONE, C_REG, C_NONE, 54, 16, 0, 0, 0},
    	{AMOVWU, C_TLS, C_NONE, C_REG, C_NONE, 54, 16, 0, sys.Loong64, 0},
    	{AMOVV, C_TLS, C_NONE, C_REG, C_NONE, 54, 16, 0, sys.Loong64, 0},
    	{AMOVB, C_TLS, C_NONE, C_REG, C_NONE, 54, 16, 0, 0, 0},
    	{AMOVBU, C_TLS, C_NONE, C_REG, C_NONE, 54, 16, 0, 0, 0},
    
    	{AMOVW, C_SECON, C_NONE, C_REG, C_NONE, 3, 4, 0, sys.Loong64, 0},
    	{AMOVV, C_SECON, C_NONE, C_REG, C_NONE, 3, 4, 0, sys.Loong64, 0},
    	{AMOVW, C_SACON, C_NONE, C_REG, C_NONE, 3, 4, REGSP, 0, 0},
    	{AMOVV, C_SACON, C_NONE, C_REG, C_NONE, 3, 4, REGSP, sys.Loong64, 0},
    	{AMOVW, C_LECON, C_NONE, C_REG, C_NONE, 52, 8, 0, 0, NOTUSETMP},
    	{AMOVW, C_LECON, C_NONE, C_REG, C_NONE, 52, 8, 0, sys.Loong64, NOTUSETMP},
    	{AMOVV, C_LECON, C_NONE, C_REG, C_NONE, 52, 8, 0, sys.Loong64, NOTUSETMP},
    
    	{AMOVW, C_LACON, C_NONE, C_REG, C_NONE, 26, 12, REGSP, 0, 0},
    	{AMOVV, C_LACON, C_NONE, C_REG, C_NONE, 26, 12, REGSP, sys.Loong64, 0},
    	{AMOVW, C_ADDCON, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0, 0},
    	{AMOVV, C_ADDCON, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, sys.Loong64, 0},
    	{AMOVW, C_ANDCON, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0, 0},
    	{AMOVV, C_ANDCON, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, sys.Loong64, 0},
    	{AMOVW, C_STCON, C_NONE, C_REG, C_NONE, 55, 12, 0, 0, 0},
    	{AMOVV, C_STCON, C_NONE, C_REG, C_NONE, 55, 12, 0, sys.Loong64, 0},
    
    	{AMOVW, C_UCON, C_NONE, C_REG, C_NONE, 24, 4, 0, 0, 0},
    	{AMOVV, C_UCON, C_NONE, C_REG, C_NONE, 24, 4, 0, sys.Loong64, 0},
    	{AMOVW, C_LCON, C_NONE, C_REG, C_NONE, 19, 8, 0, 0, NOTUSETMP},
    	{AMOVV, C_LCON, C_NONE, C_REG, C_NONE, 19, 8, 0, sys.Loong64, NOTUSETMP},
    	{AMOVV, C_DCON, C_NONE, C_REG, C_NONE, 59, 16, 0, sys.Loong64, NOTUSETMP},
    
    	{AMUL, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{AMUL, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, 0, 0},
    	{AMULV, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, sys.Loong64, 0},
    	{AMULV, C_REG, C_REG, C_REG, C_NONE, 2, 4, 0, sys.Loong64, 0},
    
    	{AADD, C_ADD0CON, C_REG, C_REG, C_NONE, 4, 4, 0, 0, 0},
    	{AADD, C_ADD0CON, C_NONE, C_REG, C_NONE, 4, 4, 0, 0, 0},
    	{AADD, C_ANDCON, C_REG, C_REG, C_NONE, 10, 8, 0, 0, 0},
    	{AADD, C_ANDCON, C_NONE, C_REG, C_NONE, 10, 8, 0, 0, 0},
    
    	{AADDV, C_ADD0CON, C_REG, C_REG, C_NONE, 4, 4, 0, sys.Loong64, 0},
    	{AADDV, C_ADD0CON, C_NONE, C_REG, C_NONE, 4, 4, 0, sys.Loong64, 0},
    	{AADDV, C_ANDCON, C_REG, C_REG, C_NONE, 10, 8, 0, sys.Loong64, 0},
    	{AADDV, C_ANDCON, C_NONE, C_REG, C_NONE, 10, 8, 0, sys.Loong64, 0},
    
    	{AAND, C_AND0CON, C_REG, C_REG, C_NONE, 4, 4, 0, 0, 0},
    	{AAND, C_AND0CON, C_NONE, C_REG, C_NONE, 4, 4, 0, 0, 0},
    	{AAND, C_ADDCON, C_REG, C_REG, C_NONE, 10, 8, 0, 0, 0},
    	{AAND, C_ADDCON, C_NONE, C_REG, C_NONE, 10, 8, 0, 0, 0},
    
    	{AADD, C_UCON, C_REG, C_REG, C_NONE, 25, 8, 0, 0, 0},
    	{AADD, C_UCON, C_NONE, C_REG, C_NONE, 25, 8, 0, 0, 0},
    	{AADDV, C_UCON, C_REG, C_REG, C_NONE, 25, 8, 0, sys.Loong64, 0},
    	{AADDV, C_UCON, C_NONE, C_REG, C_NONE, 25, 8, 0, sys.Loong64, 0},
    	{AAND, C_UCON, C_REG, C_REG, C_NONE, 25, 8, 0, 0, 0},
    	{AAND, C_UCON, C_NONE, C_REG, C_NONE, 25, 8, 0, 0, 0},
    
    	{AADD, C_LCON, C_NONE, C_REG, C_NONE, 23, 12, 0, 0, 0},
    	{AADDV, C_LCON, C_NONE, C_REG, C_NONE, 23, 12, 0, sys.Loong64, 0},
    	{AAND, C_LCON, C_NONE, C_REG, C_NONE, 23, 12, 0, 0, 0},
    	{AADD, C_LCON, C_REG, C_REG, C_NONE, 23, 12, 0, 0, 0},
    	{AADDV, C_LCON, C_REG, C_REG, C_NONE, 23, 12, 0, sys.Loong64, 0},
    	{AAND, C_LCON, C_REG, C_REG, C_NONE, 23, 12, 0, 0, 0},
    
    	{AADDV, C_DCON, C_NONE, C_REG, C_NONE, 60, 20, 0, sys.Loong64, 0},
    	{AADDV, C_DCON, C_REG, C_REG, C_NONE, 60, 20, 0, sys.Loong64, 0},
    
    	{ASLL, C_SCON, C_REG, C_REG, C_NONE, 16, 4, 0, 0, 0},
    	{ASLL, C_SCON, C_NONE, C_REG, C_NONE, 16, 4, 0, 0, 0},
    
    	{ASLLV, C_SCON, C_REG, C_REG, C_NONE, 16, 4, 0, sys.Loong64, 0},
    	{ASLLV, C_SCON, C_NONE, C_REG, C_NONE, 16, 4, 0, sys.Loong64, 0},
    
    	{ASYSCALL, C_NONE, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0, 0},
    
    	{ABEQ, C_REG, C_REG, C_SBRA, C_NONE, 6, 4, 0, 0, 0},
    	{ABEQ, C_REG, C_NONE, C_SBRA, C_NONE, 6, 4, 0, 0, 0},
    	{ABLEZ, C_REG, C_NONE, C_SBRA, C_NONE, 6, 4, 0, 0, 0},
    	{ABFPT, C_NONE, C_NONE, C_SBRA, C_NONE, 6, 4, 0, 0, NOTUSETMP},
    
    	{AJMP, C_NONE, C_NONE, C_LBRA, C_NONE, 11, 4, 0, 0, 0}, // b
    	{AJAL, C_NONE, C_NONE, C_LBRA, C_NONE, 11, 4, 0, 0, 0}, // bl
    
    	{AJMP, C_NONE, C_NONE, C_ZOREG, C_NONE, 18, 4, REGZERO, 0, 0}, // jirl r0, rj, 0
    	{AJAL, C_NONE, C_NONE, C_ZOREG, C_NONE, 18, 4, REGLINK, 0, 0}, // jirl r1, rj, 0
    
    	{AMOVW, C_SEXT, C_NONE, C_FREG, C_NONE, 27, 4, 0, sys.Loong64, 0},
    	{AMOVF, C_SEXT, C_NONE, C_FREG, C_NONE, 27, 4, 0, sys.Loong64, 0},
    	{AMOVD, C_SEXT, C_NONE, C_FREG, C_NONE, 27, 4, 0, sys.Loong64, 0},
    	{AMOVW, C_SAUTO, C_NONE, C_FREG, C_NONE, 27, 4, REGSP, sys.Loong64, 0},
    	{AMOVF, C_SAUTO, C_NONE, C_FREG, C_NONE, 27, 4, REGSP, 0, 0},
    	{AMOVD, C_SAUTO, C_NONE, C_FREG, C_NONE, 27, 4, REGSP, 0, 0},
    	{AMOVW, C_SOREG, C_NONE, C_FREG, C_NONE, 27, 4, REGZERO, sys.Loong64, 0},
    	{AMOVF, C_SOREG, C_NONE, C_FREG, C_NONE, 27, 4, REGZERO, 0, 0},
    	{AMOVD, C_SOREG, C_NONE, C_FREG, C_NONE, 27, 4, REGZERO, 0, 0},
    
    	{AMOVW, C_LEXT, C_NONE, C_FREG, C_NONE, 27, 12, 0, sys.Loong64, 0},
    	{AMOVF, C_LEXT, C_NONE, C_FREG, C_NONE, 27, 12, 0, sys.Loong64, 0},
    	{AMOVD, C_LEXT, C_NONE, C_FREG, C_NONE, 27, 12, 0, sys.Loong64, 0},
    	{AMOVW, C_LAUTO, C_NONE, C_FREG, C_NONE, 27, 12, REGSP, sys.Loong64, 0},
    	{AMOVF, C_LAUTO, C_NONE, C_FREG, C_NONE, 27, 12, REGSP, 0, 0},
    	{AMOVD, C_LAUTO, C_NONE, C_FREG, C_NONE, 27, 12, REGSP, 0, 0},
    	{AMOVW, C_LOREG, C_NONE, C_FREG, C_NONE, 27, 12, REGZERO, sys.Loong64, 0},
    	{AMOVF, C_LOREG, C_NONE, C_FREG, C_NONE, 27, 12, REGZERO, 0, 0},
    	{AMOVD, C_LOREG, C_NONE, C_FREG, C_NONE, 27, 12, REGZERO, 0, 0},
    	{AMOVF, C_ADDR, C_NONE, C_FREG, C_NONE, 51, 8, 0, 0, 0},
    	{AMOVF, C_ADDR, C_NONE, C_FREG, C_NONE, 51, 8, 0, sys.Loong64, 0},
    	{AMOVD, C_ADDR, C_NONE, C_FREG, C_NONE, 51, 8, 0, 0, 0},
    	{AMOVD, C_ADDR, C_NONE, C_FREG, C_NONE, 51, 8, 0, sys.Loong64, 0},
    
    	{AMOVW, C_FREG, C_NONE, C_SEXT, C_NONE, 28, 4, 0, sys.Loong64, 0},
    	{AMOVF, C_FREG, C_NONE, C_SEXT, C_NONE, 28, 4, 0, sys.Loong64, 0},
    	{AMOVD, C_FREG, C_NONE, C_SEXT, C_NONE, 28, 4, 0, sys.Loong64, 0},
    	{AMOVW, C_FREG, C_NONE, C_SAUTO, C_NONE, 28, 4, REGSP, sys.Loong64, 0},
    	{AMOVF, C_FREG, C_NONE, C_SAUTO, C_NONE, 28, 4, REGSP, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_SAUTO, C_NONE, 28, 4, REGSP, 0, 0},
    	{AMOVW, C_FREG, C_NONE, C_SOREG, C_NONE, 28, 4, REGZERO, sys.Loong64, 0},
    	{AMOVF, C_FREG, C_NONE, C_SOREG, C_NONE, 28, 4, REGZERO, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_SOREG, C_NONE, 28, 4, REGZERO, 0, 0},
    
    	{AMOVW, C_FREG, C_NONE, C_LEXT, C_NONE, 28, 12, 0, sys.Loong64, 0},
    	{AMOVF, C_FREG, C_NONE, C_LEXT, C_NONE, 28, 12, 0, sys.Loong64, 0},
    	{AMOVD, C_FREG, C_NONE, C_LEXT, C_NONE, 28, 12, 0, sys.Loong64, 0},
    	{AMOVW, C_FREG, C_NONE, C_LAUTO, C_NONE, 28, 12, REGSP, sys.Loong64, 0},
    	{AMOVF, C_FREG, C_NONE, C_LAUTO, C_NONE, 28, 12, REGSP, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_LAUTO, C_NONE, 28, 12, REGSP, 0, 0},
    	{AMOVW, C_FREG, C_NONE, C_LOREG, C_NONE, 28, 12, REGZERO, sys.Loong64, 0},
    	{AMOVF, C_FREG, C_NONE, C_LOREG, C_NONE, 28, 12, REGZERO, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_LOREG, C_NONE, 28, 12, REGZERO, 0, 0},
    	{AMOVF, C_FREG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0, 0},
    	{AMOVF, C_FREG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, sys.Loong64, 0},
    	{AMOVD, C_FREG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_ADDR, C_NONE, 50, 8, 0, sys.Loong64, 0},
    
    	{AMOVW, C_REG, C_NONE, C_FREG, C_NONE, 30, 4, 0, 0, 0},
    	{AMOVW, C_FREG, C_NONE, C_REG, C_NONE, 31, 4, 0, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_FREG, C_NONE, 47, 4, 0, sys.Loong64, 0},
    	{AMOVV, C_FREG, C_NONE, C_REG, C_NONE, 48, 4, 0, sys.Loong64, 0},
    
    
    	{AMOVV, C_FCCREG, C_NONE, C_REG, C_NONE, 63, 4, 0, sys.Loong64, 0},
    	{AMOVV, C_REG, C_NONE, C_FCCREG, C_NONE, 64, 4, 0, sys.Loong64, 0},
    
    
    	{AMOVW, C_ADDCON, C_NONE, C_FREG, C_NONE, 34, 8, 0, sys.Loong64, 0},
    	{AMOVW, C_ANDCON, C_NONE, C_FREG, C_NONE, 34, 8, 0, sys.Loong64, 0},
    
    	{AWORD, C_LCON, C_NONE, C_NONE, C_NONE, 40, 4, 0, 0, 0},
    	{AWORD, C_DCON, C_NONE, C_NONE, C_NONE, 61, 4, 0, 0, 0},
    
    	{ATEQ, C_SCON, C_REG, C_REG, C_NONE, 15, 8, 0, 0, 0},
    	{ATEQ, C_SCON, C_NONE, C_REG, C_NONE, 15, 8, 0, 0, 0},
    
    	{ABREAK, C_REG, C_NONE, C_SEXT, C_NONE, 7, 4, 0, sys.Loong64, 0}, // really CACHE instruction
    	{ABREAK, C_REG, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, sys.Loong64, 0},
    	{ABREAK, C_REG, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, sys.Loong64, 0},
    	{ABREAK, C_NONE, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0, 0},
    
    	{ARDTIMELW, C_NONE, C_NONE, C_REG, C_REG, 62, 4, 0, 0, 0},
    	{ARDTIMEHW, C_NONE, C_NONE, C_REG, C_REG, 62, 4, 0, 0, 0},
    	{ARDTIMED, C_NONE, C_NONE, C_REG, C_REG, 62, 4, 0, 0, 0},
    
    	{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 49, 4, 0, 0, 0},
    	{obj.APCDATA, C_LCON, C_NONE, C_LCON, C_NONE, 0, 0, 0, 0, 0},
    	{obj.APCDATA, C_DCON, C_NONE, C_DCON, C_NONE, 0, 0, 0, 0, 0},
    	{obj.AFUNCDATA, C_SCON, C_NONE, C_ADDR, C_NONE, 0, 0, 0, 0, 0},
    	{obj.ANOP, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
    	{obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // nop variants, see #40689
    	{obj.ANOP, C_DCON, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // nop variants, see #40689
    	{obj.ANOP, C_REG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
    	{obj.ANOP, C_FREG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
    	{obj.ADUFFZERO, C_NONE, C_NONE, C_LBRA, C_NONE, 11, 4, 0, 0, 0}, // same as AJMP
    	{obj.ADUFFCOPY, C_NONE, C_NONE, C_LBRA, C_NONE, 11, 4, 0, 0, 0}, // same as AJMP
    
    	{obj.AXXX, C_NONE, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0, 0},
    
    }
    
    var oprange [ALAST & obj.AMask][]Optab
    
    var xcmp [C_NCLASS][C_NCLASS]bool
    
    func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
    	if ctxt.Retpoline {
    		ctxt.Diag("-spectre=ret not supported on loong64")
    		ctxt.Retpoline = false // don't keep printing
    	}
    
    	p := cursym.Func().Text
    	if p == nil || p.Link == nil { // handle external functions and ELF section symbols
    		return
    	}
    
    	c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: int32(p.To.Offset + ctxt.Arch.FixedFrameSize)}
    
    	if oprange[AOR&obj.AMask] == nil {
    		c.ctxt.Diag("loong64 ops not initialized, call loong64.buildop first")
    	}
    
    	pc := int64(0)
    	p.Pc = pc
    
    	var m int
    	var o *Optab
    	for p = p.Link; p != nil; p = p.Link {
    		p.Pc = pc
    		o = c.oplook(p)
    		m = int(o.size)
    		if m == 0 {
    			if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA {
    				c.ctxt.Diag("zero-width instruction\n%v", p)
    			}
    			continue
    		}
    
    		pc += int64(m)
    	}
    
    	c.cursym.Size = pc
    
    	/*
    	 * if any procedure is large enough to
    	 * generate a large SBRA branch, then
    	 * generate extra passes putting branches
    	 * around jmps to fix. this is rare.
    	 */
    	bflag := 1
    
    	var otxt int64
    	var q *obj.Prog
    	for bflag != 0 {
    		bflag = 0
    		pc = 0
    		for p = c.cursym.Func().Text.Link; p != nil; p = p.Link {
    			p.Pc = pc
    			o = c.oplook(p)
    
    			// very large conditional branches
    			if o.type_ == 6 && p.To.Target() != nil {
    				otxt = p.To.Target().Pc - pc
    				if otxt < -(1<<17)+10 || otxt >= (1<<17)-10 {
    					q = c.newprog()
    					q.Link = p.Link
    					p.Link = q
    					q.As = AJMP
    					q.Pos = p.Pos
    					q.To.Type = obj.TYPE_BRANCH
    					q.To.SetTarget(p.To.Target())
    					p.To.SetTarget(q)
    					q = c.newprog()
    					q.Link = p.Link
    					p.Link = q
    					q.As = AJMP
    					q.Pos = p.Pos
    					q.To.Type = obj.TYPE_BRANCH
    					q.To.SetTarget(q.Link.Link)
    					bflag = 1
    				}
    			}
    
    			m = int(o.size)
    			if m == 0 {
    				if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA {
    					c.ctxt.Diag("zero-width instruction\n%v", p)
    				}
    				continue
    			}
    
    			pc += int64(m)
    		}
    
    		c.cursym.Size = pc
    	}
    	pc += -pc & (FuncAlign - 1)
    	c.cursym.Size = pc
    
    	// lay out the code, emitting code and data relocations.
    
    	c.cursym.Grow(c.cursym.Size)
    
    	bp := c.cursym.P
    	var i int32
    	var out [5]uint32
    	for p := c.cursym.Func().Text.Link; p != nil; p = p.Link {
    		c.pc = p.Pc
    		o = c.oplook(p)
    		if int(o.size) > 4*len(out) {
    			log.Fatalf("out array in span0 is too small, need at least %d for %v", o.size/4, p)
    		}
    		c.asmout(p, o, out[:])
    		for i = 0; i < int32(o.size/4); i++ {
    			c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i])
    			bp = bp[4:]
    		}
    	}
    
    	// Mark nonpreemptible instruction sequences.
    	// We use REGTMP as a scratch register during call injection,
    	// so instruction sequences that use REGTMP are unsafe to
    	// preempt asynchronously.
    	obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable)
    }
    
    // isUnsafePoint returns whether p is an unsafe point.
    func (c *ctxt0) isUnsafePoint(p *obj.Prog) bool {
    	// If p explicitly uses REGTMP, it's unsafe to preempt, because the
    	// preemption sequence clobbers REGTMP.
    	return p.From.Reg == REGTMP || p.To.Reg == REGTMP || p.Reg == REGTMP
    }
    
    // isRestartable returns whether p is a multi-instruction sequence that,
    // if preempted, can be restarted.
    func (c *ctxt0) isRestartable(p *obj.Prog) bool {
    	if c.isUnsafePoint(p) {
    		return false
    	}
    	// If p is a multi-instruction sequence with uses REGTMP inserted by
    	// the assembler in order to materialize a large constant/offset, we
    	// can restart p (at the start of the instruction sequence), recompute
    	// the content of REGTMP, upon async preemption. Currently, all cases
    	// of assembler-inserted REGTMP fall into this category.
    	// If p doesn't use REGTMP, it can be simply preempted, so we don't
    	// mark it.
    	o := c.oplook(p)
    	return o.size > 4 && o.flag&NOTUSETMP == 0
    }
    
    func isint32(v int64) bool {
    	return int64(int32(v)) == v
    }
    
    func isuint32(v uint64) bool {
    	return uint64(uint32(v)) == v
    }
    
    func (c *ctxt0) aclass(a *obj.Addr) int {
    	switch a.Type {
    	case obj.TYPE_NONE:
    		return C_NONE
    
    	case obj.TYPE_REG:
    		if REG_R0 <= a.Reg && a.Reg <= REG_R31 {
    			return C_REG
    		}
    		if REG_F0 <= a.Reg && a.Reg <= REG_F31 {
    			return C_FREG
    		}
    		if REG_FCSR0 <= a.Reg && a.Reg <= REG_FCSR31 {
    			return C_FCSRREG
    		}
    		if REG_FCC0 <= a.Reg && a.Reg <= REG_FCC31 {
    			return C_FCCREG
    		}
    		return C_GOK
    
    	case obj.TYPE_MEM:
    		switch a.Name {
    		case obj.NAME_EXTERN,
    			obj.NAME_STATIC:
    			if a.Sym == nil {
    				break
    			}
    			c.instoffset = a.Offset
    			if a.Sym != nil { // use relocation
    				if a.Sym.Type == objabi.STLSBSS {
    					return C_TLS
    				}
    				return C_ADDR
    			}
    			return C_LEXT
    
    		case obj.NAME_AUTO:
    			if a.Reg == REGSP {
    				// unset base register for better printing, since
    				// a.Offset is still relative to pseudo-SP.
    				a.Reg = obj.REG_NONE
    			}
    			c.instoffset = int64(c.autosize) + a.Offset
    			if c.instoffset >= -BIG && c.instoffset < BIG {
    				return C_SAUTO
    			}
    			return C_LAUTO
    
    		case obj.NAME_PARAM:
    			if a.Reg == REGSP {
    				// unset base register for better printing, since
    				// a.Offset is still relative to pseudo-FP.
    				a.Reg = obj.REG_NONE
    			}
    			c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
    			if c.instoffset >= -BIG && c.instoffset < BIG {
    				return C_SAUTO
    			}
    			return C_LAUTO
    
    		case obj.NAME_NONE:
    			c.instoffset = a.Offset
    			if c.instoffset == 0 {
    				return C_ZOREG
    			}
    			if c.instoffset >= -BIG && c.instoffset < BIG {
    				return C_SOREG
    			}
    			return C_LOREG
    		}
    
    		return C_GOK
    
    	case obj.TYPE_TEXTSIZE:
    		return C_TEXTSIZE
    
    	case obj.TYPE_CONST,
    		obj.TYPE_ADDR:
    		switch a.Name {
    		case obj.NAME_NONE:
    			c.instoffset = a.Offset
    			if a.Reg != 0 {
    				if -BIG <= c.instoffset && c.instoffset <= BIG {
    					return C_SACON
    				}
    				if isint32(c.instoffset) {
    					return C_LACON
    				}
    				return C_DACON
    			}
    
    		case obj.NAME_EXTERN,
    			obj.NAME_STATIC:
    			s := a.Sym
    			if s == nil {
    				return C_GOK
    			}
    
    			c.instoffset = a.Offset
    			if s.Type == objabi.STLSBSS {
    				return C_STCON // address of TLS variable
    			}
    			return C_LECON
    
    		case obj.NAME_AUTO:
    			if a.Reg == REGSP {
    				// unset base register for better printing, since
    				// a.Offset is still relative to pseudo-SP.
    				a.Reg = obj.REG_NONE
    			}
    			c.instoffset = int64(c.autosize) + a.Offset
    			if c.instoffset >= -BIG && c.instoffset < BIG {
    				return C_SACON
    			}
    			return C_LACON
    
    		case obj.NAME_PARAM:
    			if a.Reg == REGSP {
    				// unset base register for better printing, since
    				// a.Offset is still relative to pseudo-FP.
    				a.Reg = obj.REG_NONE
    			}
    			c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
    			if c.instoffset >= -BIG && c.instoffset < BIG {
    				return C_SACON
    			}
    			return C_LACON
    
    		default:
    			return C_GOK
    		}
    
    		if c.instoffset != int64(int32(c.instoffset)) {
    			return C_DCON
    		}
    
    		if c.instoffset >= 0 {
    			if c.instoffset == 0 {
    				return C_ZCON
    			}
    			if c.instoffset <= 0x7ff {
    				return C_SCON
    			}
    			if c.instoffset <= 0xfff {
    				return C_ANDCON
    			}
    			if c.instoffset&0xfff == 0 && isuint32(uint64(c.instoffset)) { // && (instoffset & (1<<31)) == 0)
    				return C_UCON
    			}
    			if isint32(c.instoffset) || isuint32(uint64(c.instoffset)) {
    				return C_LCON
    			}
    			return C_LCON
    		}
    
    		if c.instoffset >= -0x800 {
    			return C_ADDCON
    		}
    		if c.instoffset&0xfff == 0 && isint32(c.instoffset) {
    			return C_UCON
    		}
    		if isint32(c.instoffset) {
    			return C_LCON
    		}
    		return C_LCON
    
    	case obj.TYPE_BRANCH:
    		return C_SBRA
    	}
    
    	return C_GOK
    }
    
    func prasm(p *obj.Prog) {
    	fmt.Printf("%v\n", p)
    }
    
    func (c *ctxt0) oplook(p *obj.Prog) *Optab {
    	if oprange[AOR&obj.AMask] == nil {
    		c.ctxt.Diag("loong64 ops not initialized, call loong64.buildop first")
    	}
    
    	a1 := int(p.Optab)
    	if a1 != 0 {
    		return &optab[a1-1]
    	}
    
    	a1 = int(p.From.Class)
    	if a1 == 0 {
    		a1 = c.aclass(&p.From) + 1
    		p.From.Class = int8(a1)
    	}
    	a1--
    
    
    	// first destination operand
    
    	a3 := int(p.To.Class)
    	if a3 == 0 {
    		a3 = c.aclass(&p.To) + 1
    		p.To.Class = int8(a3)
    	}
    	a3--
    
    	// 2nd destination operand
    	a4 := C_NONE
    	if p.RegTo2 != 0 {
    		a4 = C_REG
    	}
    
    
    	ops := oprange[p.As&obj.AMask]
    	c1 := &xcmp[a1]
    	c3 := &xcmp[a3]
    	for i := range ops {
    		op := &ops[i]
    
    		if (int(op.a2) == a2) && c1[op.a1] && c3[op.a3] && (int(op.a4) == a4) {
    
    			p.Optab = uint16(cap(optab) - cap(ops) + i + 1)
    			return op
    		}
    	}
    
    
    	c.ctxt.Diag("illegal combination %v %v %v %v %v", p.As, DRconv(a1), DRconv(a2), DRconv(a3), DRconv(a4))
    
    	prasm(p)
    	// Turn illegal instruction into an UNDEF, avoid crashing in asmout.
    
    	return &Optab{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 49, 4, 0, 0, 0}
    
    }
    
    func cmp(a int, b int) bool {
    	if a == b {
    		return true
    	}
    	switch a {
    	case C_DCON:
    		if b == C_LCON {
    			return true
    		}
    		fallthrough
    	case C_LCON:
    		if b == C_ZCON || b == C_SCON || b == C_UCON || b == C_ADDCON || b == C_ANDCON {
    			return true
    		}
    
    	case C_ADD0CON:
    		if b == C_ADDCON {
    			return true
    		}
    		fallthrough
    
    	case C_ADDCON:
    		if b == C_ZCON || b == C_SCON {
    			return true
    		}
    
    	case C_AND0CON:
    		if b == C_ANDCON {
    			return true
    		}
    		fallthrough
    
    	case C_ANDCON:
    		if b == C_ZCON || b == C_SCON {
    			return true
    		}
    
    	case C_UCON:
    		if b == C_ZCON {
    			return true
    		}
    
    	case C_SCON:
    		if b == C_ZCON {
    			return true
    		}
    
    	case C_LACON:
    		if b == C_SACON {
    			return true
    		}
    
    	case C_LBRA:
    		if b == C_SBRA {
    			return true
    		}
    
    	case C_LEXT:
    		if b == C_SEXT {
    			return true
    		}
    
    	case C_LAUTO:
    		if b == C_SAUTO {
    			return true
    		}
    
    	case C_REG:
    		if b == C_ZCON {
    			return true
    		}
    
    	case C_LOREG:
    		if b == C_ZOREG || b == C_SOREG {
    			return true
    		}
    
    	case C_SOREG:
    		if b == C_ZOREG {
    			return true
    		}
    	}
    
    	return false
    }
    
    type ocmp []Optab
    
    func (x ocmp) Len() int {
    	return len(x)
    }
    
    func (x ocmp) Swap(i, j int) {
    	x[i], x[j] = x[j], x[i]
    }
    
    func (x ocmp) Less(i, j int) bool {
    	p1 := &x[i]
    	p2 := &x[j]
    	n := int(p1.as) - int(p2.as)
    	if n != 0 {
    		return n < 0
    	}
    	n = int(p1.a1) - int(p2.a1)
    	if n != 0 {
    		return n < 0
    	}
    	n = int(p1.a2) - int(p2.a2)
    	if n != 0 {
    		return n < 0
    	}
    	n = int(p1.a3) - int(p2.a3)
    	if n != 0 {
    		return n < 0
    	}
    	return false
    }
    
    func opset(a, b0 obj.As) {
    	oprange[a&obj.AMask] = oprange[b0]
    }
    
    func buildop(ctxt *obj.Link) {
    	if ctxt.DiagFunc == nil {
    		ctxt.DiagFunc = func(format string, args ...interface{}) {
    			log.Printf(format, args...)
    		}
    	}
    
    	if oprange[AOR&obj.AMask] != nil {
    		// Already initialized; stop now.
    		// This happens in the cmd/asm tests,
    		// each of which re-initializes the arch.
    		return
    	}
    
    	var n int
    
    	for i := 0; i < C_NCLASS; i++ {
    		for n = 0; n < C_NCLASS; n++ {
    			if cmp(n, i) {
    				xcmp[i][n] = true
    			}
    		}
    	}
    	for n = 0; optab[n].as != obj.AXXX; n++ {
    	}
    	sort.Sort(ocmp(optab[:n]))
    	for i := 0; i < n; i++ {
    		r := optab[i].as
    		r0 := r & obj.AMask
    		start := i
    		for optab[i].as == r {
    			i++
    		}
    		oprange[r0] = optab[start:i]
    		i--
    
    		switch r {
    		default:
    			ctxt.Diag("unknown op in build: %v", r)
    			ctxt.DiagFlush()
    			log.Fatalf("bad code")
    
    		case AABSF:
    			opset(AMOVFD, r0)
    			opset(AMOVDF, r0)
    			opset(AMOVWF, r0)
    			opset(AMOVFW, r0)
    			opset(AMOVWD, r0)
    			opset(AMOVDW, r0)
    			opset(ANEGF, r0)
    			opset(ANEGD, r0)
    			opset(AABSD, r0)
    			opset(ATRUNCDW, r0)
    			opset(ATRUNCFW, r0)
    			opset(ASQRTF, r0)
    			opset(ASQRTD, r0)
    
    		case AMOVVF:
    			opset(AMOVVD, r0)
    			opset(AMOVFV, r0)
    			opset(AMOVDV, r0)
    			opset(ATRUNCDV, r0)
    			opset(ATRUNCFV, r0)
    
    		case AADD:
    			opset(ASGT, r0)
    			opset(ASGTU, r0)
    			opset(AADDU, r0)
    
    		case AADDV:
    			opset(AADDVU, r0)
    
    		case AADDF:
    			opset(ADIVF, r0)
    			opset(ADIVD, r0)
    			opset(AMULF, r0)
    			opset(AMULD, r0)
    			opset(ASUBF, r0)
    			opset(ASUBD, r0)
    			opset(AADDD, r0)
    
    		case AAND:
    			opset(AOR, r0)
    			opset(AXOR, r0)
    
    		case ABEQ:
    			opset(ABNE, r0)
    			opset(ABLT, r0)
    			opset(ABGE, r0)
    			opset(ABGEU, r0)
    			opset(ABLTU, r0)
    
    		case ABLEZ:
    			opset(ABGEZ, r0)
    			opset(ABLTZ, r0)
    			opset(ABGTZ, r0)
    
    		case AMOVB:
    			opset(AMOVH, r0)
    
    		case AMOVBU:
    			opset(AMOVHU, r0)
    
    		case AMUL:
    			opset(AMULU, r0)
    			opset(AMULH, r0)
    			opset(AMULHU, r0)
    			opset(AREM, r0)
    			opset(AREMU, r0)
    			opset(ADIV, r0)
    			opset(ADIVU, r0)
    
    		case AMULV:
    			opset(AMULVU, r0)
    			opset(AMULHV, r0)
    			opset(AMULHVU, r0)
    			opset(AREMV, r0)
    			opset(AREMVU, r0)
    			opset(ADIVV, r0)
    			opset(ADIVVU, r0)
    
    		case ASLL:
    			opset(ASRL, r0)
    			opset(ASRA, r0)