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

View as plain text