Source file src/simd/archsimd/_gen/simdgen/arm64/instruction.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package arm64
     6  
     7  import (
     8  	"fmt"
     9  	"regexp"
    10  	"slices"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"golang.org/x/arch/arm64/instgen/xmlspec"
    16  )
    17  
    18  var (
    19  	resultInArg0Re     = regexp.MustCompile(`= V\{[^}]*\}\(d\)`)        // pseudocode reading dest register
    20  	floatPatternRe     = regexp.MustCompile(`-?(?:half|single|double)`) // docvar float type detection
    21  	fixedArrangementRe = regexp.MustCompile(`\.(\d+[BHSD])`)            // hardcoded arrangements like .16B, .4S
    22  	arngSymbolRe       = regexp.MustCompile(`\.<(T[a-z]*)>`)            // arrangement symbols like <T>, <Ta>, <Tb>
    23  )
    24  
    25  // Instruction represents a parsed ARM64 instruction with domain logic
    26  type Instruction struct {
    27  	xmlspec.Instruction               // Embedded XML data from xmlspec
    28  	arrangementsCache   []Arrangement // Cache for arrangements
    29  	mnemonicCache       string        // Cache for mnemonic
    30  	arngShape           ArngShape     // Arrangement shape
    31  }
    32  
    33  // BaseTypeSet allows to specify the type set of values independent of arrangement's size, e.g.:
    34  // - Float (instruction used for floating point values in lanes),
    35  // - Uint (instruction used only for unsigned integer values in lanes with any arrangement),
    36  // - Float|Int|Uint (e.g. VMOV V1.S[i], V0.S[j]: copy i-th lane from src vreg to j-th lane of dst vreg: basically don't care about base type).
    37  type BaseTypeSet int
    38  
    39  const (
    40  	BaseTypeInt = 1 << iota
    41  	BaseTypeUint
    42  	BaseTypeFloat
    43  )
    44  
    45  func (t BaseTypeSet) String() string {
    46  	switch t {
    47  	case BaseTypeInt:
    48  		return "int"
    49  	case BaseTypeUint:
    50  		return "uint"
    51  	case BaseTypeFloat:
    52  		return "float"
    53  	default:
    54  		return ""
    55  	}
    56  }
    57  
    58  // template defines operand templates for instruction which get instantiated for each arrangement.
    59  type template struct {
    60  	operands    []Operand
    61  	instruction *Instruction
    62  }
    63  
    64  // Arrangement defines the properties of a vector arrangement.
    65  type Arrangement struct {
    66  	arrangement string
    67  	baseType    string
    68  	elemBits    int
    69  	bits        int
    70  	lanes       int
    71  }
    72  
    73  // ArngShape makes certain vreg operands half or double bits wide.
    74  type ArngShape int
    75  
    76  const (
    77  	DefaultArngs     = ArngShape(iota) // DefaultArngs indicates that vector register arguments have the same bit width.
    78  	NarrowArngs                        // NarrowArngs signifies that the destination vector register is half the bit width of the source, used in instructions like XTN/XTN2.
    79  	LongArngs                          // LongArngs indicates that the destination vector register is double the bit width of the source, seen in instructions like UXTL/UXTL2.
    80  	WideArngs                          // WideArngs applies when the second argument vector register is half the bit width of the first argument or the result, as in UADDW.
    81  	UnsupportedArngs                   // UnsupportedArngs indicates instructions whose arrangement shape is not yet supported by simdgen.
    82  )
    83  
    84  // extractDocVar searches for a docvar by key in the instruction's docvars
    85  func (instruction *Instruction) extractDocVar(key string) string {
    86  	for _, docVar := range instruction.DocVars {
    87  		if docVar.Key == key {
    88  			return docVar.Value
    89  		}
    90  	}
    91  	return ""
    92  }
    93  
    94  // Mnemonic extracts the mnemonic from docvars
    95  func (instruction *Instruction) Mnemonic() string {
    96  	if instruction.mnemonicCache != "" {
    97  		return instruction.mnemonicCache
    98  	}
    99  
   100  	var mnemonic string
   101  	if instruction.IsAlias() {
   102  		mnemonic = instruction.extractDocVar("alias_mnemonic")
   103  	} else {
   104  		mnemonic = instruction.extractDocVar("mnemonic")
   105  	}
   106  
   107  	instruction.mnemonicCache = mnemonic
   108  	return mnemonic
   109  }
   110  
   111  // Bitwise returns true if the instruction is a bitwise operation
   112  // by detecting "Bitwise " prefix in the brief description
   113  func (instruction *Instruction) Bitwise() bool {
   114  	brief := instruction.Brief()
   115  	return strings.HasPrefix(brief, "Bitwise ")
   116  }
   117  
   118  // InstrClass returns the instruction Class from docvars
   119  func (instruction *Instruction) InstrClass() string {
   120  	return instruction.extractDocVar("instr-class")
   121  }
   122  
   123  // ResultInArg0 determines if result shares register with first argument.
   124  // This occurs when the destination register is also read as an input operand.
   125  func (instruction *Instruction) ResultInArg0() bool {
   126  	mnemonic := instruction.Mnemonic()
   127  
   128  	// For TBL/TBX the pattern looks like "= if is_tbl ... else V[d]".
   129  	if mnemonic == "TBX" {
   130  		return true
   131  	}
   132  
   133  	// Check pseudocode for reading destination register
   134  	for _, PsSection := range instruction.PsSections {
   135  		for _, Ps := range PsSection.Ps {
   136  			if slices.ContainsFunc(Ps.PSText, resultInArg0Re.MatchString) {
   137  				return true
   138  			}
   139  		}
   140  	}
   141  	return false
   142  }
   143  
   144  // IsAlias returns true if this instruction is an alias of another instruction
   145  func (instruction *Instruction) IsAlias() bool {
   146  	return instruction.Type == "alias"
   147  }
   148  
   149  // BaseTypeSet determines if an instruction operates on integers or floats
   150  func (instruction *Instruction) BaseTypeSet() BaseTypeSet {
   151  	mnemonic := instruction.Mnemonic()
   152  	// DUP and INS are special as they can operate on any base type (int, uint, float)
   153  	// depending on the arrangement and the source register. They are not easily
   154  	// categorized by the float patterns in docvars.
   155  	if mnemonic == "DUP" || mnemonic == "INS" {
   156  		return BaseTypeInt | BaseTypeUint | BaseTypeFloat
   157  	}
   158  
   159  	for _, docVar := range instruction.DocVars {
   160  		if floatPatternRe.MatchString(docVar.Value) {
   161  			return BaseTypeFloat
   162  		}
   163  	}
   164  
   165  	for _, Class := range instruction.Classes.Iclass {
   166  		for _, docVar := range Class.DocVars {
   167  			if floatPatternRe.MatchString(docVar.Value) {
   168  				return BaseTypeFloat
   169  			}
   170  		}
   171  	}
   172  
   173  	return BaseTypeInt | BaseTypeUint
   174  }
   175  
   176  // extractUMOVArrangements handles special case for UMOV instruction
   177  // UMOV has specific arrangements based on element size and vector register size
   178  func (instruction *Instruction) extractUMOVArrangements() []string {
   179  	var arrangements []string
   180  
   181  	// Check if this is the 32-bit or 64-bit variant by looking at the assembly template
   182  	var has32Bit, has64Bit bool
   183  	if len(instruction.Classes.Iclass) > 0 && len(instruction.Classes.Iclass[0].Encodings) > 0 {
   184  		for _, Encoding := range instruction.Classes.Iclass[0].Encodings {
   185  			AsmTemplate := asmTemplateToString(Encoding.AsmTemplate)
   186  			if strings.Contains(AsmTemplate, "<Wd>") {
   187  				has32Bit = true
   188  			}
   189  			if strings.Contains(AsmTemplate, "<Xd>") {
   190  				has64Bit = true
   191  			}
   192  		}
   193  	}
   194  
   195  	// Generate arrangements based on available variants
   196  	if has32Bit {
   197  		// 32-bit UMOV variants (Wd destination)
   198  		arrangements = append(arrangements, "8B", "4H", "2S")
   199  	}
   200  	if has64Bit {
   201  		// 64-bit UMOV variants (Xd destination)
   202  		arrangements = append(arrangements, "16B", "8H", "4S", "2D")
   203  	}
   204  
   205  	return arrangements
   206  }
   207  
   208  // Arrangements collects valid arrangement/type specifiers for the instruction
   209  func (instruction *Instruction) Arrangements() ([]Arrangement, ArngShape) {
   210  	if instruction.arrangementsCache != nil {
   211  		return instruction.arrangementsCache, instruction.arngShape
   212  	}
   213  	baseTypeSet := instruction.BaseTypeSet()
   214  	stringArrangements, ashape := instruction.arrangementStrings()
   215  	bitwise := instruction.Bitwise()
   216  	var arrangements []Arrangement
   217  	for ty := BaseTypeSet(BaseTypeInt); ty <= BaseTypeFloat; ty <<= 1 {
   218  		if ty&baseTypeSet == 0 {
   219  			continue
   220  		}
   221  		for _, arrStr := range stringArrangements {
   222  			elemBits, bits, lanes := parseArrangement(arrStr)
   223  			if elemBits == 0 {
   224  				continue
   225  			}
   226  			if ty == BaseTypeFloat && elemBits != 32 && elemBits != 64 {
   227  				continue
   228  			}
   229  			maxElemBits := elemBits
   230  			if bitwise {
   231  				maxElemBits = bits >> 1
   232  			}
   233  			l := lanes
   234  			for e := elemBits; e <= maxElemBits; e = e * 2 {
   235  				arrangements = append(arrangements, Arrangement{
   236  					arrangement: arrStr,
   237  					baseType:    ty.String(),
   238  					elemBits:    e,
   239  					bits:        bits,
   240  					lanes:       l,
   241  				})
   242  				l = l >> 1
   243  			}
   244  		}
   245  	}
   246  	instruction.arrangementsCache = arrangements
   247  	instruction.arngShape = ashape
   248  	return arrangements, ashape
   249  }
   250  
   251  // extractFixedArrangements extracts hardcoded arrangements from asmtemplate
   252  // (e.g., AESE with ".16B"). For instructions with variable arrangements
   253  // (e.g., ADD with "<T>"), returns empty slice.
   254  func (instruction *Instruction) extractFixedArrangements() []string {
   255  	var arrangements []string
   256  
   257  	for _, class := range instruction.Classes.Iclass {
   258  		for _, encoding := range class.Encodings {
   259  			var templateStr strings.Builder
   260  			for _, content := range encoding.AsmTemplate.TextA {
   261  				if content.Link == "sa_t" || content.Link == "sa_ta" {
   262  					return []string{}
   263  				}
   264  				templateStr.WriteString(content.Value)
   265  			}
   266  
   267  			// Extract hardcoded arrangements like ".16B", ".4S", ".8H", ".2D"
   268  			matches := fixedArrangementRe.FindAllStringSubmatch(templateStr.String(), -1)
   269  			for _, match := range matches {
   270  				if len(match) > 1 {
   271  					arrangement := match[1]
   272  					if arrangement != "" {
   273  						// Avoid non-existing fixed arrangements like 4B (e.g. see USDOT's asmtemplate).
   274  						_, bits, _ := parseArrangement(arrangement)
   275  						if bits >= 64 {
   276  							arrangements = append(arrangements, arrangement)
   277  						}
   278  					}
   279  				}
   280  			}
   281  		}
   282  	}
   283  	return removeDuplicates(arrangements)
   284  }
   285  
   286  // arrangementSymbols extracts arrangement symbols from the first encoding
   287  // that has arrangements in its assembly template.
   288  //
   289  // Variable symbols (e.g., T, Ta, Tb) appear as <T>, <Ta>, <Tb> in templates.
   290  // Hardcoded arrangements (e.g., 16B, 2D) appear as .16B, .2D in templates.
   291  //
   292  // Returns nil if no arrangements are found at all.
   293  // Returns single-element (e.g., ["T"] or ["16B"]) for uniform/fixed instructions.
   294  // Returns multiple elements (e.g., ["Ta","Tb"] or ["Ta","2D"]) for non-uniform
   295  // instructions where operand widths differ.
   296  func (instruction *Instruction) arrangementSymbols() []string {
   297  	for _, class := range instruction.Classes.Iclass {
   298  		for _, encoding := range class.Encodings {
   299  			templateStr := asmTemplateToString(encoding.AsmTemplate)
   300  			if !strings.Contains(templateStr, ">.") {
   301  				continue
   302  			}
   303  
   304  			seen := make(map[string]bool)
   305  			var symbols []string
   306  
   307  			// Extract variable arrangement symbols (e.g., T, Ta, Tb, Ts).
   308  			for _, m := range arngSymbolRe.FindAllStringSubmatch(templateStr, -1) {
   309  				sym := m[1]
   310  				if !seen[sym] {
   311  					seen[sym] = true
   312  					symbols = append(symbols, sym)
   313  				}
   314  			}
   315  
   316  			// Extract hardcoded arrangements (e.g., 16B, 4S, 2D).
   317  			for _, m := range fixedArrangementRe.FindAllStringSubmatch(templateStr, -1) {
   318  				arr := m[1]
   319  				if !seen[arr] {
   320  					seen[arr] = true
   321  					symbols = append(symbols, arr)
   322  				}
   323  			}
   324  
   325  			if len(symbols) > 0 {
   326  				return symbols
   327  			}
   328  		}
   329  	}
   330  	return nil
   331  }
   332  
   333  // arrangementStrings extracts arrangement specifiers from XML explanations as strings
   334  func (instruction *Instruction) arrangementStrings() ([]string, ArngShape) {
   335  	var arrangements []string
   336  
   337  	mnemonic := instruction.Mnemonic()
   338  	ashape := DefaultArngs
   339  
   340  	switch mnemonic {
   341  	case "UMOV":
   342  		return instruction.extractUMOVArrangements(), DefaultArngs
   343  
   344  	case "INS":
   345  		// INS instruction inserts general register values into vector elements
   346  		// It supports all arrangements: B, H, S, D with 128-bit vector registers
   347  		arrangements = append(arrangements, "16B", "8H", "4S", "2D")
   348  		return arrangements, DefaultArngs
   349  	}
   350  
   351  	// Determine the arrangement shape and which symbol to extract from.
   352  	// For LongArngs and NarrowArngs, we need only the source-side symbol.
   353  	// For WideArngs, we need only the wide-side symbol.
   354  	ashape = instruction.ArngShape()
   355  	var targetSymbol string
   356  	if ashape == LongArngs || ashape == NarrowArngs {
   357  		symbols := instruction.arrangementSymbols()
   358  		if len(symbols) >= 2 {
   359  			targetSymbol = "<" + symbols[len(symbols)-1] + ">"
   360  		}
   361  	} else if ashape == WideArngs {
   362  		symbols := instruction.arrangementSymbols()
   363  		if len(symbols) >= 2 {
   364  			targetSymbol = "<" + symbols[0] + ">"
   365  		}
   366  	}
   367  
   368  	nonTarget := map[string]bool{}
   369  	for _, Explanation := range instruction.Explanations.Explanations {
   370  		Definition := Explanation.Definition
   371  		if Definition.Table.TGroup.TBody.Row != nil {
   372  			isTarget := targetSymbol == "" || targetSymbol == strings.TrimSpace(Explanation.Symbol.Value)
   373  			for _, Row := range Definition.Table.TGroup.TBody.Row {
   374  				for _, Entry := range Row.Entries {
   375  					if Entry.Class == "symbol" {
   376  						v := strings.TrimSpace(Entry.Value)
   377  						if isTarget {
   378  							arrangements = append(arrangements, v)
   379  						} else if eb, _, _ := parseArrangement(v); eb > 0 {
   380  							nonTarget[v] = false
   381  						}
   382  					}
   383  				}
   384  			}
   385  		}
   386  	}
   387  
   388  	verifyNonTargetArrangements(instruction.Mnemonic(), ashape, arrangements, nonTarget)
   389  
   390  	fixedArrangements := instruction.extractFixedArrangements()
   391  	arrangements = append(arrangements, fixedArrangements...)
   392  	arrangements = removeDuplicates(arrangements)
   393  	return arrangements, ashape
   394  }
   395  
   396  // verifyNonTargetArrangements checks that non-target symbol arrangements are the
   397  // expected transformed versions of the target arrangements (half/double elemBits).
   398  func verifyNonTargetArrangements(mnemonic string, ashape ArngShape, target []string, nonTarget map[string]bool) {
   399  	if ashape == DefaultArngs || len(nonTarget) == 0 {
   400  		return
   401  	}
   402  	// FCVTN has a FEAT_FP8 variant not covered by NarrowArngs.
   403  	// The other variants are covered.
   404  	switch mnemonic {
   405  	case "FCVTN", "FCVTXN":
   406  		return
   407  	}
   408  	for _, t := range target {
   409  		eb, _, _ := parseArrangement(t)
   410  		if eb == 0 {
   411  			continue
   412  		}
   413  		var expectedElemBits int
   414  		switch ashape {
   415  		case LongArngs:
   416  			expectedElemBits = eb * 2
   417  		case NarrowArngs, WideArngs:
   418  			expectedElemBits = eb / 2
   419  		}
   420  		if expectedElemBits == 0 {
   421  			continue
   422  		}
   423  		for nt := range nonTarget {
   424  			ntEb, _, _ := parseArrangement(nt)
   425  			if ntEb == expectedElemBits {
   426  				nonTarget[nt] = true
   427  			}
   428  		}
   429  	}
   430  	var unexplained []string
   431  	for nt, explained := range nonTarget {
   432  		if !explained {
   433  			unexplained = append(unexplained, nt)
   434  		}
   435  	}
   436  	if len(unexplained) > 0 {
   437  		sort.Strings(unexplained)
   438  		panic(fmt.Sprintf("%s: non-target arrangements not explained by target: %v\ntarget: %v",
   439  			mnemonic, unexplained, target))
   440  	}
   441  }
   442  
   443  // regDiagramArngShape returns the expected arrangement shape based on RegDiagram for NEON.
   444  // Used for cross-check verification by ArngShape() only.
   445  func (instruction *Instruction) regDiagramArngShape() ArngShape {
   446  	for _, class := range instruction.Classes.Iclass {
   447  		psName := class.RegDiagram.PsName
   448  		if strings.Contains(psName, "_L.") || strings.HasSuffix(psName, "_L") ||
   449  			strings.HasSuffix(psName, "_P") { // _P = pairwise long (e.g., SADALP, SADDLP)
   450  			return LongArngs
   451  		}
   452  		if strings.Contains(psName, "_W.") || strings.HasSuffix(psName, "_W") {
   453  			return WideArngs
   454  		}
   455  		if strings.Contains(psName, "_N.") || strings.HasSuffix(psName, "_N") {
   456  			return NarrowArngs
   457  		}
   458  	}
   459  	switch instruction.Mnemonic() {
   460  	case "FCVTN":
   461  		return NarrowArngs
   462  	case "SHLL":
   463  		return LongArngs
   464  	}
   465  	return DefaultArngs
   466  }
   467  
   468  // ArngShape returns the arrangement shape.
   469  // Returns UnsupportedArngs for instructions we don't support yet - those will not be emitted.
   470  // If we were not able to classify the instruction, panic to prevent wrong yaml generation.
   471  func (instruction *Instruction) ArngShape() ArngShape {
   472  	symbols := instruction.arrangementSymbols()
   473  	if symbols == nil {
   474  		return UnsupportedArngs
   475  	}
   476  
   477  	for _, s := range symbols {
   478  		switch s {
   479  		case "T", "Ta", "Tb":
   480  			// Known variable arrangement symbols.
   481  		case "Ts":
   482  			// Element-wise, not yet supported.
   483  			return UnsupportedArngs
   484  		default:
   485  			// Fixed arrangements (e.g., "16B", "4S", "2D").
   486  			if !fixedArrangementRe.MatchString("." + s) {
   487  				panic(fmt.Sprintf("ArngShape: unknown arrangement symbol %q in %q (symbols: %v)", s, instruction.Mnemonic(), symbols))
   488  			}
   489  		}
   490  	}
   491  
   492  	var shape ArngShape
   493  	if len(symbols) == 1 {
   494  		if symbols[0] == "Ta" || symbols[0] == "Tb" {
   495  			panic(fmt.Sprintf("ArngShape: unexpected lone %q in %q (symbols: %v)", symbols[0], instruction.Mnemonic(), symbols))
   496  		}
   497  		shape = DefaultArngs
   498  	} else {
   499  		switch mnemonic := instruction.Mnemonic(); mnemonic {
   500  		// NarrowArngs: destination register is half the width of source(s).
   501  		case "XTN", "SQXTN", "UQXTN", "SQXTUN",
   502  			"ADDHN", "SUBHN", "RADDHN", "RSUBHN",
   503  			"SHRN", "RSHRN",
   504  			"SQSHRN", "SQRSHRN", "SQSHRUN", "SQRSHRUN",
   505  			"UQSHRN", "UQRSHRN",
   506  			"FCVTN", "FCVTXN":
   507  			shape = NarrowArngs
   508  
   509  		// LongArngs: destination register is double the width of source.
   510  		case "SXTL", "UXTL",
   511  			"SADDLP", "UADDLP", "SADALP", "UADALP",
   512  			"SMULL", "UMULL", "PMULL",
   513  			"SADDL", "UADDL", "SSUBL", "USUBL",
   514  			"SSHLL", "USHLL", "SHLL",
   515  			"SABAL", "UABAL", "SABDL", "UABDL",
   516  			"SMLAL", "UMLAL", "SMLSL", "UMLSL",
   517  			"SQDMLAL", "SQDMLSL", "SQDMULL",
   518  			"FCVTL":
   519  			shape = LongArngs
   520  
   521  		// WideArngs: second input operand is half the width of first input/result.
   522  		case "SADDW", "UADDW", "SSUBW", "USUBW":
   523  			shape = WideArngs
   524  
   525  		// These are not yet supported by simdgen.
   526  		case "USDOT", "SDOT", "UDOT", "SUDOT",
   527  			"BFDOT", "FDOT",
   528  			"MLA", "MLS", "MUL", "PMUL",
   529  			"FCMLA", "FCADD",
   530  			"BFCVTN",
   531  			"BFMLAL", "BFMMLA",
   532  			"FMMLA", "SMMLA", "UMMLA", "USMMLA":
   533  			return UnsupportedArngs
   534  
   535  		// TBL/TBX have a fixed .16B on table and variable <Ta> on destination/index.
   536  		case "TBL", "TBX":
   537  			shape = DefaultArngs
   538  
   539  		default:
   540  			panic(fmt.Sprintf(
   541  				"ArngShape: unhandled non-uniform arrangement instruction %q (symbols: %v)",
   542  				mnemonic, symbols))
   543  		}
   544  	}
   545  
   546  	if shape != instruction.regDiagramArngShape() {
   547  		panic(fmt.Sprintf("ArngShape: cross-check failed for %q (symbols: %v): %v but regDiagram says %v",
   548  			instruction.Mnemonic(), symbols, shape, instruction.regDiagramArngShape()))
   549  	}
   550  
   551  	return shape
   552  }
   553  
   554  // removeDuplicates removes duplicate strings from a slice
   555  func removeDuplicates(slice []string) []string {
   556  	keys := make(map[string]bool)
   557  	result := []string{}
   558  
   559  	for _, item := range slice {
   560  		if _, value := keys[item]; !value {
   561  			keys[item] = true
   562  			result = append(result, item)
   563  		}
   564  	}
   565  
   566  	return result
   567  }
   568  
   569  // parseArrangement gets element bits and lanes number from arrangement string like "4S", "2D", "16B"
   570  func parseArrangement(arrangement string) (elemBits, bits, lanes int) {
   571  	if len(arrangement) < 2 {
   572  		return 0, 0, 0
   573  	}
   574  
   575  	lanesStr := arrangement[:len(arrangement)-1]
   576  	elemType := arrangement[len(arrangement)-1:]
   577  
   578  	lanes, err := strconv.Atoi(lanesStr)
   579  	if err != nil {
   580  		return 0, 0, 0
   581  	}
   582  
   583  	switch elemType {
   584  	case "B": // Byte
   585  		elemBits = 8
   586  	case "H": // Halfword
   587  		elemBits = 16
   588  	case "S": // Single word
   589  		elemBits = 32
   590  	case "D": // Double word
   591  		elemBits = 64
   592  	default:
   593  		return 0, 0, 0
   594  	}
   595  
   596  	return elemBits, elemBits * lanes, lanes
   597  }
   598  
   599  // asmTemplateToString converts an xmlspec.AsmTemplate structure to a string
   600  func asmTemplateToString(template xmlspec.AsmTemplate) string {
   601  	var result strings.Builder
   602  	for _, content := range template.TextA {
   603  		result.WriteString(content.Value)
   604  	}
   605  	return result.String()
   606  }
   607  
   608  // templates returns operand templates
   609  func (instruction *Instruction) templates() []template {
   610  	var operands []Operand
   611  	AsmTemplate := ""
   612  searchTemplate:
   613  	for _, Class := range instruction.Classes.Iclass {
   614  		for _, Encoding := range Class.Encodings {
   615  			curAsmTemplate := asmTemplateToString(Encoding.AsmTemplate)
   616  			if strings.Contains(curAsmTemplate, ">.") && curAsmTemplate != AsmTemplate {
   617  				AsmTemplate = curAsmTemplate
   618  				break searchTemplate
   619  			}
   620  		}
   621  	}
   622  	operands = instruction.operands(AsmTemplate)
   623  	return []template{{operands: operands, instruction: instruction}}
   624  }
   625  
   626  // Documentation extracts detailed instruction documentation from the XML
   627  func (instruction *Instruction) Documentation() string {
   628  	documentation := instruction.Title
   629  	if len(instruction.Desc.Authored.Paragraphs) > 0 {
   630  		documentation = instruction.Desc.Authored.Paragraphs[0].Text
   631  	}
   632  	return documentation
   633  }
   634  
   635  // Brief returns the brief description from XML
   636  func (instruction *Instruction) Brief() string {
   637  	if len(instruction.Desc.Brief.Para) > 0 {
   638  		return strings.TrimSpace(instruction.Desc.Brief.Para[0].Text)
   639  	}
   640  	return ""
   641  }
   642  

View as plain text