diff --git a/asm/assemble.go b/asm/assemble.go index f0f2648..1f7dd71 100644 --- a/asm/assemble.go +++ b/asm/assemble.go @@ -22,6 +22,11 @@ import ( // FP/SP frame-relative operands, and local-label jumps. SB (global symbol) // operands require relocations and are not yet supported; the SIMD (VEX/AVX2) // integer and shuffle/extract/permute/move set is in. +// +// Like the other architectures, the stack-growth guard (the morestack check +// in the prologue and the call back into the runtime in the epilogue) is not +// emitted: the bytes match go tool asm only for NOSPLIT functions or +// zero-frame leaves, where the toolchain emits no guard either. func Assemble(t *ast.Text) ([]byte, map[string]int, error) { code, _, labels, _, _, err := assemble(t, nil) return code, labels, err @@ -31,7 +36,7 @@ func Assemble(t *ast.Text) ([]byte, map[string]int, error) { // the set of static symbols a GLOBL in the same file defines. A nil link // rejects SB operands outright (single-function assembly cannot resolve // them). When allowExternal is set, a reference to a symbol no GLOBL in the -// file defines is recorded as an external relocation instead of failing — +// file defines is recorded as an external relocation instead of failing // the object-file emitters resolve it at link time. type linkInfo struct { symbols map[string]bool @@ -258,14 +263,11 @@ func epilogueBytes(size int) []byte { } func subSP(size int) []byte { // SUBQ $size, SP + // imm8 holds -128..127; anything larger takes the imm32 form, exactly as + // the Go assembler encodes it (verified for 8, 128, 200 and 255). if size >= -128 && size <= 127 { return []byte{0x48, 0x83, 0xEC, byte(int8(size))} } - // 128..255 do not fit SUB's unsigned imm8, but the Go assembler - // switches to ADDQ $-size, SP whose sign-extended imm8 does. - if size >= -255 && size <= 255 { - return []byte{0x48, 0x83, 0xC4, byte(int8(-size))} - } return append([]byte{0x48, 0x81, 0xEC}, le32(int64(size))...) } diff --git a/asm/assemble_test.go b/asm/assemble_test.go index e8cefcb..e0956a1 100644 --- a/asm/assemble_test.go +++ b/asm/assemble_test.go @@ -4,6 +4,7 @@ package asm import ( + "bytes" "strings" "testing" @@ -351,3 +352,26 @@ TEXT ·pf(SB), NOSPLIT, $0 t.Errorf("PREFETCHT0 bytes: got %s, want 0f 18 0b", hex) } } + +// TestSubSPEncodings pins the prologue SUB against the bytes go tool asm +// emits for SUBQ $size, SP: imm8 for -128..127, the imm32 form for anything +// larger. The intermediate 129..255 range used to encode an ADD with a +// truncated immediate, moving SP the wrong way. +func TestSubSPEncodings(t *testing.T) { + for _, tt := range []struct { + size int + want []byte + }{ + {8, []byte{0x48, 0x83, 0xEC, 0x08}}, + {127, []byte{0x48, 0x83, 0xEC, 0x7F}}, + {128, []byte{0x48, 0x81, 0xEC, 0x80, 0x00, 0x00, 0x00}}, + {200, []byte{0x48, 0x81, 0xEC, 0xC8, 0x00, 0x00, 0x00}}, + {255, []byte{0x48, 0x81, 0xEC, 0xFF, 0x00, 0x00, 0x00}}, + {4096, []byte{0x48, 0x81, 0xEC, 0x00, 0x10, 0x00, 0x00}}, + } { + got := subSP(tt.size) + if !bytes.Equal(got, tt.want) { + t.Errorf("subSP(%d) = %x, want %x", tt.size, got, tt.want) + } + } +}