Skip to content
Snippets Groups Projects
asm.go 62 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"
    	"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
    
    	as    obj.As
    	from1 uint8
    	reg   uint8
    	from3 uint8
    	to1   uint8
    	to2   uint8
    	type_ int8
    	size  int8
    	param int16
    	flag  uint8
    
    }
    
    const (
    	NOTUSETMP = 1 << iota // p expands to multiple instructions, but does NOT use REGTMP
    
    
    	// branchLoopHead marks loop entry.
    	// Used to insert padding for under-aligned loops.
    	branchLoopHead
    
    	{obj.ATEXT, C_ADDR, C_NONE, C_NONE, C_TEXTSIZE, C_NONE, 0, 0, 0, 0},
    
    	{AMOVW, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 1, 4, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 1, 4, 0, 0},
    	{AMOVB, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 12, 8, 0, NOTUSETMP},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 13, 4, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 14, 8, 0, NOTUSETMP},
    
    	{ASUB, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ASUBV, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AADD, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AADDV, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AAND, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ASUB, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ASUBV, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AADD, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AADDV, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AAND, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ANEGW, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ANEGV, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AMASKEQZ, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    
    	{ASLL, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ASLL, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ASLLV, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{ASLLV, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AMUL, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AMUL, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AMULV, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AMULV, C_REG, C_REG, C_NONE, C_REG, C_NONE, 2, 4, 0, 0},
    	{AADDF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 2, 4, 0, 0},
    	{AADDF, C_FREG, C_FREG, C_NONE, C_FREG, C_NONE, 2, 4, 0, 0},
    	{ACMPEQF, C_FREG, C_FREG, C_NONE, C_FCCREG, C_NONE, 2, 4, 0, 0},
    
    
    	{ACLO, C_REG, C_NONE, C_NONE, C_REG, C_NONE, 9, 4, 0, 0},
    
    	{AABSF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0},
    	{AMOVVF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0},
    	{AMOVF, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_NONE, C_FREG, C_NONE, 9, 4, 0, 0},
    
    	{AMOVW, C_REG, C_NONE, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0},
    	{AMOVB, C_REG, C_NONE, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_SAUTO, C_NONE, 7, 4, REGSP, 0},
    	{AMOVW, C_REG, C_NONE, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0},
    	{AMOVB, C_REG, C_NONE, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0},
    	{ASC, C_REG, C_NONE, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0},
    	{ASCV, C_REG, C_NONE, C_NONE, C_SOREG, C_NONE, 7, 4, REGZERO, 0},
    
    	{AMOVW, C_SAUTO, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0},
    	{AMOVWU, C_SAUTO, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0},
    	{AMOVV, C_SAUTO, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0},
    	{AMOVB, C_SAUTO, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0},
    	{AMOVBU, C_SAUTO, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGSP, 0},
    	{AMOVW, C_SOREG, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0},
    	{AMOVWU, C_SOREG, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0},
    	{AMOVV, C_SOREG, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0},
    	{AMOVB, C_SOREG, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0},
    	{AMOVBU, C_SOREG, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0},
    	{ALL, C_SOREG, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0},
    	{ALLV, C_SOREG, C_NONE, C_NONE, C_REG, C_NONE, 8, 4, REGZERO, 0},
    
    	{AMOVW, C_REG, C_NONE, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0},
    	{AMOVB, C_REG, C_NONE, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_LAUTO, C_NONE, 35, 12, REGSP, 0},
    	{AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0},
    	{AMOVB, C_REG, C_NONE, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0},
    	{ASC, C_REG, C_NONE, C_NONE, C_LOREG, C_NONE, 35, 12, REGZERO, 0},
    	{AMOVW, C_REG, C_NONE, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0},
    	{AMOVB, C_REG, C_NONE, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0},
    
    	{AMOVW, C_REG, C_NONE, C_NONE, C_TLS_LE, C_NONE, 53, 16, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_TLS_LE, C_NONE, 53, 16, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_TLS_LE, C_NONE, 53, 16, 0, 0},
    	{AMOVB, C_REG, C_NONE, C_NONE, C_TLS_LE, C_NONE, 53, 16, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_TLS_LE, C_NONE, 53, 16, 0, 0},
    
    
    	{AMOVW, C_LAUTO, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0},
    	{AMOVWU, C_LAUTO, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0},
    	{AMOVV, C_LAUTO, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0},
    	{AMOVB, C_LAUTO, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0},
    	{AMOVBU, C_LAUTO, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGSP, 0},
    	{AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0},
    	{AMOVWU, C_LOREG, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0},
    	{AMOVV, C_LOREG, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0},
    	{AMOVB, C_LOREG, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0},
    	{AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, C_NONE, 36, 12, REGZERO, 0},
    	{AMOVW, C_ADDR, C_NONE, C_NONE, C_REG, C_NONE, 51, 8, 0, 0},
    	{AMOVWU, C_ADDR, C_NONE, C_NONE, C_REG, C_NONE, 51, 8, 0, 0},
    	{AMOVV, C_ADDR, C_NONE, C_NONE, C_REG, C_NONE, 51, 8, 0, 0},
    	{AMOVB, C_ADDR, C_NONE, C_NONE, C_REG, C_NONE, 51, 8, 0, 0},
    	{AMOVBU, C_ADDR, C_NONE, C_NONE, C_REG, C_NONE, 51, 8, 0, 0},
    
    	{AMOVW, C_TLS_LE, C_NONE, C_NONE, C_REG, C_NONE, 54, 16, 0, 0},
    	{AMOVWU, C_TLS_LE, C_NONE, C_NONE, C_REG, C_NONE, 54, 16, 0, 0},
    	{AMOVV, C_TLS_LE, C_NONE, C_NONE, C_REG, C_NONE, 54, 16, 0, 0},
    	{AMOVB, C_TLS_LE, C_NONE, C_NONE, C_REG, C_NONE, 54, 16, 0, 0},
    	{AMOVBU, C_TLS_LE, C_NONE, C_NONE, C_REG, C_NONE, 54, 16, 0, 0},
    
    
    	{AMOVW, C_SACON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGSP, 0},
    	{AMOVV, C_SACON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGSP, 0},
    
    	{AMOVW, C_EXTADDR, C_NONE, C_NONE, C_REG, C_NONE, 52, 8, 0, NOTUSETMP},
    	{AMOVV, C_EXTADDR, C_NONE, C_NONE, C_REG, C_NONE, 52, 8, 0, NOTUSETMP},
    
    
    	{AMOVW, C_LACON, C_NONE, C_NONE, C_REG, C_NONE, 26, 12, REGSP, 0},
    	{AMOVV, C_LACON, C_NONE, C_NONE, C_REG, C_NONE, 26, 12, REGSP, 0},
    	{AMOVW, C_ADDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0},
    	{AMOVV, C_ADDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0},
    	{AMOVW, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0},
    	{AMOVV, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 3, 4, REGZERO, 0},
    
    	{AMOVW, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 24, 4, 0, 0},
    	{AMOVV, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 24, 4, 0, 0},
    	{AMOVW, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP},
    	{AMOVV, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 19, 8, 0, NOTUSETMP},
    	{AMOVV, C_DCON, C_NONE, C_NONE, C_REG, C_NONE, 59, 16, 0, NOTUSETMP},
    
    	{AADD, C_ADD0CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0},
    	{AADD, C_ADD0CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0},
    	{AADD, C_ANDCON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0},
    	{AADD, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0},
    
    	{AADDV, C_ADD0CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0},
    	{AADDV, C_ADD0CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0},
    	{AADDV, C_ANDCON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0},
    	{AADDV, C_ANDCON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0},
    
    	{AAND, C_AND0CON, C_REG, C_NONE, C_REG, C_NONE, 4, 4, 0, 0},
    	{AAND, C_AND0CON, C_NONE, C_NONE, C_REG, C_NONE, 4, 4, 0, 0},
    	{AAND, C_ADDCON, C_REG, C_NONE, C_REG, C_NONE, 10, 8, 0, 0},
    	{AAND, C_ADDCON, C_NONE, C_NONE, C_REG, C_NONE, 10, 8, 0, 0},
    
    	{AADD, C_UCON, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0},
    	{AADD, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0},
    	{AADDV, C_UCON, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0},
    	{AADDV, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0},
    	{AAND, C_UCON, C_REG, C_NONE, C_REG, C_NONE, 25, 8, 0, 0},
    	{AAND, C_UCON, C_NONE, C_NONE, C_REG, C_NONE, 25, 8, 0, 0},
    
    	{AADD, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0},
    	{AADDV, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0},
    	{AAND, C_LCON, C_NONE, C_NONE, C_REG, C_NONE, 23, 12, 0, 0},
    	{AADD, C_LCON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0},
    	{AADDV, C_LCON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0},
    	{AAND, C_LCON, C_REG, C_NONE, C_REG, C_NONE, 23, 12, 0, 0},
    
    	{AADDV, C_DCON, C_NONE, C_NONE, C_REG, C_NONE, 60, 20, 0, 0},
    	{AADDV, C_DCON, C_REG, C_NONE, C_REG, C_NONE, 60, 20, 0, 0},
    
    	{ASLL, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 16, 4, 0, 0},
    	{ASLL, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 16, 4, 0, 0},
    
    	{ASLLV, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 16, 4, 0, 0},
    	{ASLLV, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 16, 4, 0, 0},
    
    
    	{ABSTRPICKW, C_SCON, C_REG, C_SCON, C_REG, C_NONE, 17, 4, 0, 0},
    	{ABSTRPICKW, C_SCON, C_REG, C_ZCON, C_REG, C_NONE, 17, 4, 0, 0},
    	{ABSTRPICKW, C_ZCON, C_REG, C_ZCON, C_REG, C_NONE, 17, 4, 0, 0},
    
    
    	{ASYSCALL, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0},
    
    	{ASYSCALL, C_ANDCON, C_NONE, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0},
    
    	{ABEQ, C_REG, C_REG, C_NONE, C_BRAN, C_NONE, 6, 4, 0, 0},
    	{ABEQ, C_REG, C_NONE, C_NONE, C_BRAN, C_NONE, 6, 4, 0, 0},
    	{ABLEZ, C_REG, C_NONE, C_NONE, C_BRAN, C_NONE, 6, 4, 0, 0},
    	{ABFPT, C_NONE, C_NONE, C_NONE, C_BRAN, C_NONE, 6, 4, 0, NOTUSETMP},
    
    	{AJMP, C_NONE, C_NONE, C_NONE, C_BRAN, C_NONE, 11, 4, 0, 0}, // b
    	{AJAL, C_NONE, C_NONE, C_NONE, C_BRAN, C_NONE, 11, 4, 0, 0}, // bl
    
    
    	{AJMP, C_NONE, C_NONE, C_NONE, C_ZOREG, C_NONE, 18, 4, REGZERO, 0}, // jirl r0, rj, 0
    	{AJAL, C_NONE, C_NONE, C_NONE, C_ZOREG, C_NONE, 18, 4, REGLINK, 0}, // jirl r1, rj, 0
    
    	{AMOVF, C_SAUTO, C_NONE, C_NONE, C_FREG, C_NONE, 27, 4, REGSP, 0},
    	{AMOVD, C_SAUTO, C_NONE, C_NONE, C_FREG, C_NONE, 27, 4, REGSP, 0},
    	{AMOVF, C_SOREG, C_NONE, C_NONE, C_FREG, C_NONE, 27, 4, REGZERO, 0},
    	{AMOVD, C_SOREG, C_NONE, C_NONE, C_FREG, C_NONE, 27, 4, REGZERO, 0},
    
    	{AMOVF, C_LAUTO, C_NONE, C_NONE, C_FREG, C_NONE, 27, 12, REGSP, 0},
    	{AMOVD, C_LAUTO, C_NONE, C_NONE, C_FREG, C_NONE, 27, 12, REGSP, 0},
    	{AMOVF, C_LOREG, C_NONE, C_NONE, C_FREG, C_NONE, 27, 12, REGZERO, 0},
    	{AMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, C_NONE, 27, 12, REGZERO, 0},
    	{AMOVF, C_ADDR, C_NONE, C_NONE, C_FREG, C_NONE, 51, 8, 0, 0},
    	{AMOVD, C_ADDR, C_NONE, C_NONE, C_FREG, C_NONE, 51, 8, 0, 0},
    
    	{AMOVF, C_FREG, C_NONE, C_NONE, C_SAUTO, C_NONE, 28, 4, REGSP, 0},
    	{AMOVD, C_FREG, C_NONE, C_NONE, C_SAUTO, C_NONE, 28, 4, REGSP, 0},
    	{AMOVF, C_FREG, C_NONE, C_NONE, C_SOREG, C_NONE, 28, 4, REGZERO, 0},
    	{AMOVD, C_FREG, C_NONE, C_NONE, C_SOREG, C_NONE, 28, 4, REGZERO, 0},
    
    	{AMOVF, C_FREG, C_NONE, C_NONE, C_LAUTO, C_NONE, 28, 12, REGSP, 0},
    	{AMOVD, C_FREG, C_NONE, C_NONE, C_LAUTO, C_NONE, 28, 12, REGSP, 0},
    	{AMOVF, C_FREG, C_NONE, C_NONE, C_LOREG, C_NONE, 28, 12, REGZERO, 0},
    	{AMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, C_NONE, 28, 12, REGZERO, 0},
    	{AMOVF, C_FREG, C_NONE, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_NONE, C_ADDR, C_NONE, 50, 8, 0, 0},
    
    	{AMOVW, C_REG, C_NONE, C_NONE, C_FREG, C_NONE, 30, 4, 0, 0},
    
    	{AMOVV, C_REG, C_NONE, C_NONE, C_FREG, C_NONE, 30, 4, 0, 0},
    	{AMOVW, C_FREG, C_NONE, C_NONE, C_REG, C_NONE, 30, 4, 0, 0},
    	{AMOVV, C_FREG, C_NONE, C_NONE, C_REG, C_NONE, 30, 4, 0, 0},
    	{AMOVV, C_FCCREG, C_NONE, C_NONE, C_REG, C_NONE, 30, 4, 0, 0},
    	{AMOVV, C_FCSRREG, C_NONE, C_NONE, C_REG, C_NONE, 30, 4, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_FCCREG, C_NONE, 30, 4, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_FCSRREG, C_NONE, 30, 4, 0, 0},
    	{AMOVV, C_FREG, C_NONE, C_NONE, C_FCCREG, C_NONE, 30, 4, 0, 0},
    	{AMOVV, C_FCCREG, C_NONE, C_NONE, C_FREG, C_NONE, 30, 4, 0, 0},
    
    
    	{AMOVW, C_ADDCON, C_NONE, C_NONE, C_FREG, C_NONE, 34, 8, 0, 0},
    	{AMOVW, C_ANDCON, C_NONE, C_NONE, C_FREG, C_NONE, 34, 8, 0, 0},
    
    
    	{AMOVB, C_REG, C_NONE, C_NONE, C_TLS_IE, C_NONE, 56, 16, 0, 0},
    	{AMOVW, C_REG, C_NONE, C_NONE, C_TLS_IE, C_NONE, 56, 16, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_TLS_IE, C_NONE, 56, 16, 0, 0},
    	{AMOVBU, C_REG, C_NONE, C_NONE, C_TLS_IE, C_NONE, 56, 16, 0, 0},
    	{AMOVWU, C_REG, C_NONE, C_NONE, C_TLS_IE, C_NONE, 56, 16, 0, 0},
    
    	{AMOVB, C_TLS_IE, C_NONE, C_NONE, C_REG, C_NONE, 57, 16, 0, 0},
    	{AMOVW, C_TLS_IE, C_NONE, C_NONE, C_REG, C_NONE, 57, 16, 0, 0},
    	{AMOVV, C_TLS_IE, C_NONE, C_NONE, C_REG, C_NONE, 57, 16, 0, 0},
    	{AMOVBU, C_TLS_IE, C_NONE, C_NONE, C_REG, C_NONE, 57, 16, 0, 0},
    	{AMOVWU, C_TLS_IE, C_NONE, C_NONE, C_REG, C_NONE, 57, 16, 0, 0},
    
    
    	{AWORD, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 40, 4, 0, 0},
    	{AWORD, C_DCON, C_NONE, C_NONE, C_NONE, C_NONE, 61, 4, 0, 0},
    
    
    	{AMOVV, C_GOTADDR, C_NONE, C_NONE, C_REG, C_NONE, 65, 8, 0, 0},
    
    
    	{ATEQ, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 15, 8, 0, 0},
    	{ATEQ, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 15, 8, 0, 0},
    
    	{ARDTIMELW, C_NONE, C_NONE, C_NONE, C_REG, C_REG, 62, 4, 0, 0},
    
    	{AAMSWAPW, C_REG, C_NONE, C_NONE, C_ZOREG, C_REG, 66, 4, 0, 0},
    
    	{ANOOP, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 49, 4, 0, 0},
    
    
    	/* store with extended register offset */
    	{AMOVB, C_REG, C_NONE, C_NONE, C_ROFF, C_NONE, 20, 4, 0, 0},
    	{AMOVW, C_REG, C_NONE, C_NONE, C_ROFF, C_NONE, 20, 4, 0, 0},
    	{AMOVV, C_REG, C_NONE, C_NONE, C_ROFF, C_NONE, 20, 4, 0, 0},
    	{AMOVF, C_FREG, C_NONE, C_NONE, C_ROFF, C_NONE, 20, 4, 0, 0},
    	{AMOVD, C_FREG, C_NONE, C_NONE, C_ROFF, C_NONE, 20, 4, 0, 0},
    
    	/* load with extended register offset */
    	{AMOVB, C_ROFF, C_NONE, C_NONE, C_REG, C_NONE, 21, 4, 0, 0},
    	{AMOVBU, C_ROFF, C_NONE, C_NONE, C_REG, C_NONE, 21, 4, 0, 0},
    	{AMOVW, C_ROFF, C_NONE, C_NONE, C_REG, C_NONE, 21, 4, 0, 0},
    	{AMOVWU, C_ROFF, C_NONE, C_NONE, C_REG, C_NONE, 21, 4, 0, 0},
    	{AMOVV, C_ROFF, C_NONE, C_NONE, C_REG, C_NONE, 21, 4, 0, 0},
    	{AMOVF, C_ROFF, C_NONE, C_NONE, C_FREG, C_NONE, 21, 4, 0, 0},
    	{AMOVD, C_ROFF, C_NONE, C_NONE, C_FREG, C_NONE, 21, 4, 0, 0},
    
    
    	{obj.APCALIGN, C_SCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0},
    
    	{obj.APCDATA, C_LCON, C_NONE, C_NONE, C_LCON, C_NONE, 0, 0, 0, 0},
    	{obj.APCDATA, C_DCON, C_NONE, C_NONE, C_DCON, C_NONE, 0, 0, 0, 0},
    	{obj.AFUNCDATA, C_SCON, C_NONE, C_NONE, C_ADDR, C_NONE, 0, 0, 0, 0},
    	{obj.ANOP, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0},
    	{obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, // nop variants, see #40689
    	{obj.ANOP, C_DCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0}, // nop variants, see #40689
    	{obj.ANOP, C_REG, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0},
    	{obj.ANOP, C_FREG, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0},
    
    	{obj.ADUFFZERO, C_NONE, C_NONE, C_NONE, C_BRAN, C_NONE, 11, 4, 0, 0}, // same as AJMP
    	{obj.ADUFFCOPY, C_NONE, C_NONE, C_NONE, C_BRAN, C_NONE, 11, 4, 0, 0}, // same as AJMP
    
    
    	{obj.AXXX, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0},
    
    var atomicInst = map[obj.As]uint32{
    	AAMSWAPB:   0x070B8 << 15, // amswap.b
    	AAMSWAPH:   0x070B9 << 15, // amswap.h
    	AAMSWAPW:   0x070C0 << 15, // amswap.w
    	AAMSWAPV:   0x070C1 << 15, // amswap.d
    	AAMCASB:    0x070B0 << 15, // amcas.b
    	AAMCASH:    0x070B1 << 15, // amcas.h
    	AAMCASW:    0x070B2 << 15, // amcas.w
    	AAMCASV:    0x070B3 << 15, // amcas.d
    	AAMADDW:    0x070C2 << 15, // amadd.w
    	AAMADDV:    0x070C3 << 15, // amadd.d
    	AAMANDW:    0x070C4 << 15, // amand.w
    	AAMANDV:    0x070C5 << 15, // amand.d
    	AAMORW:     0x070C6 << 15, // amor.w
    	AAMORV:     0x070C7 << 15, // amor.d
    	AAMXORW:    0x070C8 << 15, // amxor.w
    	AAMXORV:    0x070C9 << 15, // amxor.d
    	AAMMAXW:    0x070CA << 15, // ammax.w
    	AAMMAXV:    0x070CB << 15, // ammax.d
    	AAMMINW:    0x070CC << 15, // ammin.w
    	AAMMINV:    0x070CD << 15, // ammin.d
    	AAMMAXWU:   0x070CE << 15, // ammax.wu
    	AAMMAXVU:   0x070CF << 15, // ammax.du
    	AAMMINWU:   0x070D0 << 15, // ammin.wu
    	AAMMINVU:   0x070D1 << 15, // ammin.du
    	AAMSWAPDBB: 0x070BC << 15, // amswap_db.b
    	AAMSWAPDBH: 0x070BD << 15, // amswap_db.h
    	AAMSWAPDBW: 0x070D2 << 15, // amswap_db.w
    	AAMSWAPDBV: 0x070D3 << 15, // amswap_db.d
    	AAMCASDBB:  0x070B4 << 15, // amcas_db.b
    	AAMCASDBH:  0x070B5 << 15, // amcas_db.h
    	AAMCASDBW:  0x070B6 << 15, // amcas_db.w
    	AAMCASDBV:  0x070B7 << 15, // amcas_db.d
    	AAMADDDBW:  0x070D4 << 15, // amadd_db.w
    	AAMADDDBV:  0x070D5 << 15, // amadd_db.d
    	AAMANDDBW:  0x070D6 << 15, // amand_db.w
    	AAMANDDBV:  0x070D7 << 15, // amand_db.d
    	AAMORDBW:   0x070D8 << 15, // amor_db.w
    	AAMORDBV:   0x070D9 << 15, // amor_db.d
    	AAMXORDBW:  0x070DA << 15, // amxor_db.w
    	AAMXORDBV:  0x070DB << 15, // amxor_db.d
    	AAMMAXDBW:  0x070DC << 15, // ammax_db.w
    	AAMMAXDBV:  0x070DD << 15, // ammax_db.d
    	AAMMINDBW:  0x070DE << 15, // ammin_db.w
    	AAMMINDBV:  0x070DF << 15, // ammin_db.d
    	AAMMAXDBWU: 0x070E0 << 15, // ammax_db.wu
    	AAMMAXDBVU: 0x070E1 << 15, // ammax_db.du
    	AAMMINDBWU: 0x070E2 << 15, // ammin_db.wu
    	AAMMINDBVU: 0x070E3 << 15, // ammin_db.du
    }
    
    func IsAtomicInst(as obj.As) bool {
    	_, ok := atomicInst[as]
    
    	return ok
    }
    
    
    // pcAlignPadLength returns the number of bytes required to align pc to alignedValue,
    // reporting an error if alignedValue is not a power of two or is out of range.
    func pcAlignPadLength(ctxt *obj.Link, pc int64, alignedValue int64) int {
    	if !((alignedValue&(alignedValue-1) == 0) && 8 <= alignedValue && alignedValue <= 2048) {
    		ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", alignedValue)
    	}
    	return int(-pc & (alignedValue - 1))
    }
    
    
    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 {
    
    			switch p.As {
    			case obj.APCALIGN:
    				alignedValue := p.From.Offset
    				m = pcAlignPadLength(ctxt, pc, alignedValue)
    				// Update the current text symbol alignment value.
    				if int32(alignedValue) > cursym.Func().Align {
    					cursym.Func().Align = int32(alignedValue)
    				}
    				break
    			case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
    				continue
    			default:
    
    				c.ctxt.Diag("zero-width instruction\n%v", p)
    			}
    		}
    
    		pc += int64(m)
    	}
    
    	c.cursym.Size = pc
    
    
    	// mark loop entry instructions for padding
    	// loop entrances are defined as targets of backward branches
    	for p = c.cursym.Func().Text.Link; p != nil; p = p.Link {
    		if q := p.To.Target(); q != nil && q.Pc < p.Pc {
    			q.Mark |= branchLoopHead
    		}
    	}
    
    	// Run these passes until convergence.
    
    		prev := c.cursym.Func().Text
    		for p = prev.Link; p != nil; prev, p = p, p.Link {
    
    			// Prepend a PCALIGN $loopAlign to each of the loop heads
    			// that need padding, if not already done so (because this
    			// pass may execute more than once).
    			//
    			// This needs to come before any pass that look at pc,
    			// because pc will be adjusted if padding happens.
    			if p.Mark&branchLoopHead != 0 && pc&(loopAlign-1) != 0 &&
    				!(prev.As == obj.APCALIGN && prev.From.Offset >= loopAlign) {
    
    				prev.Link = q
    				q.Link = p
    				q.Pc = pc
    				q.As = obj.APCALIGN
    				q.From.Type = obj.TYPE_CONST
    				q.From.Offset = loopAlign
    				// Don't associate the synthesized PCALIGN with
    				// the original source position, for deterministic
    				// mapping between source and corresponding asm.
    				// q.Pos = p.Pos
    
    				// Manually make the PCALIGN come into effect,
    				// since this loop iteration is for p.
    				pc += int64(pcAlignPadLength(ctxt, pc, loopAlign))
    				p.Pc = pc
    
    			// very large conditional branches
    
    			// 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.
    
    			if o.type_ == 6 && p.To.Target() != nil {
    
    				otxt := p.To.Target().Pc - pc
    
    				// On loong64, the immediate value field of the conditional branch instructions
    				// BFPT and BFPT is 21 bits, and the others are 16 bits. The jump target address
    				// is to logically shift the immediate value in the instruction code to the left
    				// by 2 bits and then sign extend.
    				bound := int64(1 << (18 - 1))
    
    				switch p.As {
    				case ABFPT, ABFPF:
    					bound = int64(1 << (23 - 1))
    				}
    
    				if otxt < -bound || otxt >= bound {
    					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)
    
    				switch p.As {
    				case obj.APCALIGN:
    					alignedValue := p.From.Offset
    					m = pcAlignPadLength(ctxt, pc, alignedValue)
    					break
    				case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
    					continue
    				default:
    
    					c.ctxt.Diag("zero-width instruction\n%v", p)
    				}
    			}
    
    			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)
    		}
    
    		if p.As == obj.APCALIGN {
    			alignedValue := p.From.Offset
    			v := pcAlignPadLength(c.ctxt, p.Pc, alignedValue)
    			for i = 0; i < int32(v/4); i++ {
    				// emit ANOOP instruction by the padding size
    
    				c.ctxt.Arch.ByteOrder.PutUint32(bp, OP_12IRR(c.opirr(AAND), 0, 0, 0))
    
    		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:
    
    
    	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.Type == objabi.STLSBSS {
    				if c.ctxt.Flag_shared {
    					return C_TLS_IE
    				} else {
    					return C_TLS_LE
    
    
    		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:
    
    			if a.Index != 0 {
    				if a.Offset != 0 {
    					return C_GOK
    				}
    				// register offset
    				return C_ROFF
    			}
    
    
    			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 {
    
    				c.ctxt.Diag("taking address of TLS variable is not supported")
    
    
    		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:
    
    // In Loong64,there are 8 CFRs, denoted as fcc0-fcc7.
    // There are 4 FCSRs, denoted as fcsr0-fcsr3.
    
    func (c *ctxt0) rclass(r int16) int {
    	switch {
    	case REG_R0 <= r && r <= REG_R31:
    		return C_REG
    	case REG_F0 <= r && r <= REG_F31:
    		return C_FREG
    
    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
    
    	a4 := int(p.To.Class)
    	if a4 == 0 {
    		a4 = c.aclass(&p.To) + 1
    		p.To.Class = int8(a4)
    
    	// 2nd destination operand
    
    		a5 = C_REG
    	}
    
    	// 3rd source operand
    	a3 := C_NONE
    	if len(p.RestArgs) > 0 {
    		a3 = int(p.RestArgs[0].Class)
    		if a3 == 0 {
    			a3 = c.aclass(&p.RestArgs[0].Addr) + 1
    			p.RestArgs[0].Class = int8(a3)
    		}
    		a3--
    
    	ops := oprange[p.As&obj.AMask]
    	c1 := &xcmp[a1]
    
    		if (int(op.reg) == a2) && int(op.from3) == a3 && c1[op.from1] && c4[op.to1] && (int(op.to2) == a5) {
    
    			p.Optab = uint16(cap(optab) - cap(ops) + i + 1)
    			return op
    		}
    	}
    
    
    	c.ctxt.Diag("illegal combination %v %v %v %v %v %v", p.As, DRconv(a1), DRconv(a2), DRconv(a3), DRconv(a4), DRconv(a5))
    
    	prasm(p)
    	// Turn illegal instruction into an UNDEF, avoid crashing in asmout.
    
    	return &Optab{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 49, 4, 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_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
    	}
    
    	if n != 0 {
    		return n < 0
    	}
    	return false
    }
    
    func opset(a, b0 obj.As) {
    	oprange[a&obj.AMask] = oprange[b0]