runtime: add support for linux/arm64

Change-Id: Ibda6a5bedaff57fd161d63fc04ad260931d34413
Reviewed-on: https://go-review.googlesource.com/7142
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/src/runtime/arch1_arm64.go b/src/runtime/arch1_arm64.go
new file mode 100644
index 0000000..49a56b6
--- /dev/null
+++ b/src/runtime/arch1_arm64.go
@@ -0,0 +1,16 @@
+// Copyright 2011 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 runtime
+
+const (
+	thechar           = '7'
+	_BigEndian        = 0
+	_CacheLineSize    = 32
+	_RuntimeGogoBytes = 64
+	_PhysPageSize     = 4096
+	_PCQuantum        = 4
+	_Int64Align       = 8
+	hugePageSize      = 0
+)
diff --git a/src/runtime/arch_arm64.go b/src/runtime/arch_arm64.go
new file mode 100644
index 0000000..270cd7b
--- /dev/null
+++ b/src/runtime/arch_arm64.go
@@ -0,0 +1,8 @@
+// Copyright 2014 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 runtime
+
+type uintreg uint64
+type intptr int64 // TODO(rsc): remove
diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s
new file mode 100644
index 0000000..0d3363e
--- /dev/null
+++ b/src/runtime/asm_arm64.s
@@ -0,0 +1,1061 @@
+// Copyright 2015 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.
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "funcdata.h"
+#include "textflag.h"
+
+TEXT runtime·rt0_go(SB),NOSPLIT,$0
+	// SP = stack; R0 = argc; R1 = argv
+
+	// initialize essential registers
+	BL	runtime·reginit(SB)
+
+	SUB	$32, RSP
+	MOVW	R0, 8(RSP) // argc
+	MOVD	R1, 16(RSP) // argv
+
+	// create istack out of the given (operating system) stack.
+	// _cgo_init may update stackguard.
+	MOVD	$runtime·g0(SB), g
+	MOVD RSP, R7
+	MOVD	$(-64*1024)(R7), R0
+	MOVD	R0, g_stackguard0(g)
+	MOVD	R0, g_stackguard1(g)
+	MOVD	R0, (g_stack+stack_lo)(g)
+	MOVD	R7, (g_stack+stack_hi)(g)
+
+	// if there is a _cgo_init, call it using the gcc ABI.
+	MOVD	_cgo_init(SB), R12
+	CMP	$0, R12
+	BEQ	nocgo
+
+	BL	runtime·abort(SB)
+
+nocgo:
+	// update stackguard after _cgo_init
+	MOVD	(g_stack+stack_lo)(g), R0
+	ADD	$const__StackGuard, R0
+	MOVD	R0, g_stackguard0(g)
+	MOVD	R0, g_stackguard1(g)
+
+	// set the per-goroutine and per-mach "registers"
+	MOVD	$runtime·m0(SB), R0
+
+	// save m->g0 = g0
+	MOVD	g, m_g0(R0)
+	// save m0 to g0->m
+	MOVD	R0, g_m(g)
+
+	BL	runtime·check(SB)
+
+	MOVW	8(RSP), R0	// copy argc
+	MOVW	R0, -8(RSP)
+	MOVD	16(RSP), R0		// copy argv
+	MOVD	R0, 0(RSP)
+	BL	runtime·args(SB)
+	BL	runtime·osinit(SB)
+	BL	runtime·schedinit(SB)
+
+	// create a new goroutine to start program
+	MOVD	$runtime·main·f(SB), R0		// entry
+	MOVD	RSP, R7
+	MOVD.W	$0, -8(R7)
+	MOVD.W	R0, -8(R7)
+	MOVD.W	$0, -8(R7)
+	MOVD.W	$0, -8(R7)
+	MOVD	R7, RSP
+	BL	runtime·newproc(SB)
+	ADD	$32, RSP
+
+	// start this M
+	BL	runtime·mstart(SB)
+
+	MOVD	$0, R0
+	MOVD	R0, (R0)	// boom
+	UNDEF
+
+DATA	runtime·main·f+0(SB)/8,$runtime·main(SB)
+GLOBL	runtime·main·f(SB),RODATA,$8
+
+TEXT runtime·breakpoint(SB),NOSPLIT,$-8-0
+	BRK
+	RET
+
+TEXT runtime·asminit(SB),NOSPLIT,$-8-0
+	RET
+
+TEXT runtime·reginit(SB),NOSPLIT,$-8-0
+	// initialize essential FP registers
+	FMOVD	$4503601774854144.0, F27
+	FMOVD	$0.5, F29
+	FSUBD	F29, F29, F28
+	FADDD	F29, F29, F30
+	FADDD	F30, F30, F31
+	RET
+
+/*
+ *  go-routine
+ */
+
+// void gosave(Gobuf*)
+// save state in Gobuf; setjmp
+TEXT runtime·gosave(SB), NOSPLIT, $-8-8
+	MOVD	buf+0(FP), R3
+	MOVD	RSP, R0
+	MOVD	R0, gobuf_sp(R3)
+	MOVD	LR, gobuf_pc(R3)
+	MOVD	g, gobuf_g(R3)
+	MOVD	ZR, gobuf_lr(R3)
+	MOVD	ZR, gobuf_ret(R3)
+	MOVD	ZR, gobuf_ctxt(R3)
+	RET
+
+// void gogo(Gobuf*)
+// restore state from Gobuf; longjmp
+TEXT runtime·gogo(SB), NOSPLIT, $-8-8
+	MOVD	buf+0(FP), R5
+	MOVD	gobuf_g(R5), g
+	BL	runtime·save_g(SB)
+
+	MOVD	0(g), R4	// make sure g is not nil
+	MOVD	gobuf_sp(R5), R0
+	MOVD	R0, RSP
+	MOVD	gobuf_lr(R5), LR
+	MOVD	gobuf_ret(R5), R0
+	MOVD	gobuf_ctxt(R5), R26
+	MOVD	$0, gobuf_sp(R5)
+	MOVD	$0, gobuf_ret(R5)
+	MOVD	$0, gobuf_lr(R5)
+	MOVD	$0, gobuf_ctxt(R5)
+	CMP	ZR, ZR // set condition codes for == test, needed by stack split
+	MOVD	gobuf_pc(R5), R6
+	B	(R6)
+
+// void mcall(fn func(*g))
+// Switch to m->g0's stack, call fn(g).
+// Fn must never return.  It should gogo(&g->sched)
+// to keep running g.
+TEXT runtime·mcall(SB), NOSPLIT, $-8-8
+	// Save caller state in g->sched
+	MOVD	RSP, R0
+	MOVD	R0, (g_sched+gobuf_sp)(g)
+	MOVD	LR, (g_sched+gobuf_pc)(g)
+	MOVD	$0, (g_sched+gobuf_lr)(g)
+	MOVD	g, (g_sched+gobuf_g)(g)
+
+	// Switch to m->g0 & its stack, call fn.
+	MOVD	g, R3
+	MOVD	g_m(g), R8
+	MOVD	m_g0(R8), g
+	BL	runtime·save_g(SB)
+	CMP	g, R3
+	BNE	2(PC)
+	B	runtime·badmcall(SB)
+	MOVD	fn+0(FP), R26			// context
+	MOVD	0(R26), R4			// code pointer
+	MOVD	(g_sched+gobuf_sp)(g), R0
+	MOVD	R0, RSP	// sp = m->g0->sched.sp
+	MOVD	R3, -8(RSP)
+	MOVD	$0, -16(RSP)
+	SUB	$16, RSP
+	BL	(R4)
+	B	runtime·badmcall2(SB)
+
+// systemstack_switch is a dummy routine that systemstack leaves at the bottom
+// of the G stack.  We need to distinguish the routine that
+// lives at the bottom of the G stack from the one that lives
+// at the top of the system stack because the one at the top of
+// the system stack terminates the stack walk (see topofstack()).
+TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
+	UNDEF
+	BL	(LR)	// make sure this function is not leaf
+	RET
+
+// func systemstack(fn func())
+TEXT runtime·systemstack(SB), NOSPLIT, $0-8
+	MOVD	fn+0(FP), R3	// R3 = fn
+	MOVD	R3, R26		// context
+	MOVD	g_m(g), R4	// R4 = m
+
+	MOVD	m_gsignal(R4), R5	// R5 = gsignal
+	CMP	g, R5
+	BEQ	noswitch
+
+	MOVD	m_g0(R4), R5	// R5 = g0
+	CMP	g, R5
+	BEQ	noswitch
+
+	MOVD	m_curg(R4), R6
+	CMP	g, R6
+	BEQ	switch
+
+	// Bad: g is not gsignal, not g0, not curg. What is it?
+	// Hide call from linker nosplit analysis.
+	MOVD	$runtime·badsystemstack(SB), R3
+	BL	(R3)
+
+switch:
+	// save our state in g->sched.  Pretend to
+	// be systemstack_switch if the G stack is scanned.
+	MOVD	$runtime·systemstack_switch(SB), R6
+	ADD	$8, R6	// get past prologue
+	MOVD	R6, (g_sched+gobuf_pc)(g)
+	MOVD	RSP, R0
+	MOVD	R0, (g_sched+gobuf_sp)(g)
+	MOVD	$0, (g_sched+gobuf_lr)(g)
+	MOVD	g, (g_sched+gobuf_g)(g)
+
+	// switch to g0
+	MOVD	R5, g
+	BL	runtime·save_g(SB)
+	MOVD	(g_sched+gobuf_sp)(g), R3
+	// make it look like mstart called systemstack on g0, to stop traceback
+	SUB	$16, R3
+	AND	$~15, R3
+	MOVD	$runtime·mstart(SB), R4
+	MOVD	R4, 0(R3)
+	MOVD	R3, RSP
+
+	// call target function
+	MOVD	0(R26), R3	// code pointer
+	BL	(R3)
+
+	// switch back to g
+	MOVD	g_m(g), R3
+	MOVD	m_curg(R3), g
+	BL	runtime·save_g(SB)
+	MOVD	(g_sched+gobuf_sp)(g), R0
+	MOVD	R0, RSP
+	MOVD	$0, (g_sched+gobuf_sp)(g)
+	RET
+
+noswitch:
+	// already on m stack, just call directly
+	MOVD	0(R26), R3	// code pointer
+	BL	(R3)
+	RET
+
+/*
+ * support for morestack
+ */
+
+// Called during function prolog when more stack is needed.
+// Caller has already loaded:
+// R3 prolog's LR (R30)
+//
+// The traceback routines see morestack on a g0 as being
+// the top of a stack (for example, morestack calling newstack
+// calling the scheduler calling newm calling gc), so we must
+// record an argument size. For that purpose, it has no arguments.
+TEXT runtime·morestack(SB),NOSPLIT,$-8-0
+	// Cannot grow scheduler stack (m->g0).
+	MOVD	g_m(g), R8
+	MOVD	m_g0(R8), R4
+	CMP	g, R4
+	BNE	2(PC)
+	B	runtime·abort(SB)
+
+	// Cannot grow signal stack (m->gsignal).
+	MOVD	m_gsignal(R8), R4
+	CMP	g, R4
+	BNE	2(PC)
+	B	runtime·abort(SB)
+
+	// Called from f.
+	// Set g->sched to context in f
+	MOVD	R26, (g_sched+gobuf_ctxt)(g)
+	MOVD	RSP, R0
+	MOVD	R0, (g_sched+gobuf_sp)(g)
+	MOVD	LR, (g_sched+gobuf_pc)(g)
+	MOVD	R3, (g_sched+gobuf_lr)(g)
+
+	// Called from f.
+	// Set m->morebuf to f's callers.
+	MOVD	R3, (m_morebuf+gobuf_pc)(R8)	// f's caller's PC
+	MOVD	RSP, R0
+	MOVD	R0, (m_morebuf+gobuf_sp)(R8)	// f's caller's RSP
+	MOVD	g, (m_morebuf+gobuf_g)(R8)
+
+	// Call newstack on m->g0's stack.
+	MOVD	m_g0(R8), g
+	BL	runtime·save_g(SB)
+	MOVD	(g_sched+gobuf_sp)(g), R0
+	MOVD	R0, RSP
+	BL	runtime·newstack(SB)
+
+	// Not reached, but make sure the return PC from the call to newstack
+	// is still in this function, and not the beginning of the next.
+	UNDEF
+
+TEXT runtime·morestack_noctxt(SB),NOSPLIT,$-4-0
+	MOVW	$0, R26
+	B runtime·morestack(SB)
+
+// reflectcall: call a function with the given argument list
+// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// we don't have variable-sized frames, so we use a small number
+// of constant-sized-frame functions to encode a few bits of size in the pc.
+// Caution: ugly multiline assembly macros in your future!
+
+#define DISPATCH(NAME,MAXSIZE)		\
+	MOVD	$MAXSIZE, R27;		\
+	CMP	R27, R16;		\
+	BGT	3(PC);			\
+	MOVD	$NAME(SB), R27;	\
+	B	(R27)
+// Note: can't just "B NAME(SB)" - bad inlining results.
+
+TEXT reflect·call(SB), NOSPLIT, $0-0
+	B	·reflectcall(SB)
+
+TEXT ·reflectcall(SB), NOSPLIT, $-8-32
+	MOVWU argsize+24(FP), R16
+	// NOTE(rsc): No call16, because CALLFN needs four words
+	// of argument space to invoke callwritebarrier.
+	DISPATCH(runtime·call32, 32)
+	DISPATCH(runtime·call64, 64)
+	DISPATCH(runtime·call128, 128)
+	DISPATCH(runtime·call256, 256)
+	DISPATCH(runtime·call512, 512)
+	DISPATCH(runtime·call1024, 1024)
+	DISPATCH(runtime·call2048, 2048)
+	DISPATCH(runtime·call4096, 4096)
+	DISPATCH(runtime·call8192, 8192)
+	DISPATCH(runtime·call16384, 16384)
+	DISPATCH(runtime·call32768, 32768)
+	DISPATCH(runtime·call65536, 65536)
+	DISPATCH(runtime·call131072, 131072)
+	DISPATCH(runtime·call262144, 262144)
+	DISPATCH(runtime·call524288, 524288)
+	DISPATCH(runtime·call1048576, 1048576)
+	DISPATCH(runtime·call2097152, 2097152)
+	DISPATCH(runtime·call4194304, 4194304)
+	DISPATCH(runtime·call8388608, 8388608)
+	DISPATCH(runtime·call16777216, 16777216)
+	DISPATCH(runtime·call33554432, 33554432)
+	DISPATCH(runtime·call67108864, 67108864)
+	DISPATCH(runtime·call134217728, 134217728)
+	DISPATCH(runtime·call268435456, 268435456)
+	DISPATCH(runtime·call536870912, 536870912)
+	DISPATCH(runtime·call1073741824, 1073741824)
+	MOVD	$runtime·badreflectcall(SB), R0
+	B	(R0)
+
+#define CALLFN(NAME,MAXSIZE)			\
+TEXT NAME(SB), WRAPPER, $MAXSIZE-24;		\
+	NO_LOCAL_POINTERS;			\
+	/* copy arguments to stack */		\
+	MOVD	arg+16(FP), R3;			\
+	MOVWU	argsize+24(FP), R4;			\
+	MOVD	RSP, R5;				\
+	ADD	$(8-1), R5;			\
+	SUB	$1, R3;				\
+	ADD	R5, R4;				\
+	CMP	R5, R4;				\
+	BEQ	4(PC);				\
+	MOVBU.W	1(R3), R6;			\
+	MOVBU.W	R6, 1(R5);			\
+	B	-4(PC);				\
+	/* call function */			\
+	MOVD	f+8(FP), R26;			\
+	MOVD	(R26), R0;			\
+	PCDATA  $PCDATA_StackMapIndex, $0;	\
+	BL	(R0);				\
+	/* copy return values back */		\
+	MOVD	arg+16(FP), R3;			\
+	MOVWU	n+24(FP), R4;			\
+	MOVWU	retoffset+28(FP), R6;		\
+	MOVD	RSP, R5;				\
+	ADD	R6, R5; 			\
+	ADD	R6, R3;				\
+	SUB	R6, R4;				\
+	ADD	$(8-1), R5;			\
+	SUB	$1, R3;				\
+	ADD	R5, R4;				\
+loop:						\
+	CMP	R5, R4;				\
+	BEQ	end;				\
+	MOVBU.W	1(R5), R6;			\
+	MOVBU.W	R6, 1(R3);			\
+	B	loop;				\
+end:						\
+	/* execute write barrier updates */	\
+	MOVD	argtype+0(FP), R7;		\
+	MOVD	arg+16(FP), R3;			\
+	MOVWU	n+24(FP), R4;			\
+	MOVWU	retoffset+28(FP), R6;		\
+	MOVD	R7, 8(RSP);			\
+	MOVD	R3, 16(RSP);			\
+	MOVD	R4, 24(RSP);			\
+	MOVD	R6, 32(RSP);			\
+	BL	runtime·callwritebarrier(SB);	\
+	RET
+
+CALLFN(·call16, 16)
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
+
+// bool cas(uint32 *ptr, uint32 old, uint32 new)
+// Atomically:
+//	if(*val == old){
+//		*val = new;
+//		return 1;
+//	} else
+//		return 0;
+TEXT runtime·cas(SB), NOSPLIT, $0-17
+	MOVD	ptr+0(FP), R0
+	MOVW	old+8(FP), R1
+	MOVW	new+12(FP), R2
+again:
+	LDAXRW	(R0), R3
+	CMPW	R1, R3
+	BNE	ok
+	STLXRW	R2, (R0), R3
+	CBNZ	R3, again
+ok:
+	CSET	EQ, R0
+	MOVB	R0, ret+16(FP)
+	RET
+
+TEXT runtime·casuintptr(SB), NOSPLIT, $0-25
+	B	runtime·cas64(SB)
+
+TEXT runtime·atomicloaduintptr(SB), NOSPLIT, $-8-16
+	B	runtime·atomicload64(SB)
+
+TEXT runtime·atomicloaduint(SB), NOSPLIT, $-8-16
+	B	runtime·atomicload64(SB)
+
+TEXT runtime·atomicstoreuintptr(SB), NOSPLIT, $0-16
+	B	runtime·atomicstore64(SB)
+
+// bool casp(void **val, void *old, void *new)
+// Atomically:
+//	if(*val == old){
+//		*val = new;
+//		return 1;
+//	} else
+//		return 0;
+TEXT runtime·casp1(SB), NOSPLIT, $0-25
+	B runtime·cas64(SB)
+
+TEXT runtime·procyield(SB),NOSPLIT,$0-0
+	MOVWU	cycles+0(FP), R0
+again:
+	YIELD
+	SUBW	$1, R0
+	CBNZ	R0, again
+	RET
+
+// void jmpdefer(fv, sp);
+// called from deferreturn.
+// 1. grab stored LR for caller
+// 2. sub 4 bytes to get back to BL deferreturn
+// 3. BR to fn
+TEXT runtime·jmpdefer(SB), NOSPLIT, $-8-16
+	MOVD	0(RSP), R0
+	SUB	$4, R0
+	MOVD	R0, LR
+
+	MOVD	fv+0(FP), R26
+	MOVD	argp+8(FP), R0
+	MOVD	R0, RSP
+	SUB	$8, RSP
+	MOVD	0(R26), R3
+	B	(R3)
+
+// Save state of caller into g->sched. Smashes R0.
+TEXT gosave<>(SB),NOSPLIT,$-8
+	MOVD	LR, (g_sched+gobuf_pc)(g)
+	MOVD RSP, R0
+	MOVD	R0, (g_sched+gobuf_sp)(g)
+	MOVD	$0, (g_sched+gobuf_lr)(g)
+	MOVD	$0, (g_sched+gobuf_ret)(g)
+	MOVD	$0, (g_sched+gobuf_ctxt)(g)
+	RET
+
+// asmcgocall(void(*fn)(void*), void *arg)
+// Call fn(arg) on the scheduler stack,
+// aligned appropriately for the gcc ABI.
+// See cgocall.c for more details.
+TEXT ·asmcgocall(SB),NOSPLIT,$0-16
+	MOVD	fn+0(FP), R3
+	MOVD	arg+8(FP), R4
+	BL	asmcgocall<>(SB)
+	RET
+
+TEXT ·asmcgocall_errno(SB),NOSPLIT,$0-20
+	MOVD	fn+0(FP), R3
+	MOVD	arg+8(FP), R4
+	BL	asmcgocall<>(SB)
+	MOVW	R0, ret+16(FP)
+	RET
+
+// asmcgocall common code. fn in R3, arg in R4. returns errno in R0.
+TEXT asmcgocall<>(SB),NOSPLIT,$0-0
+	MOVD	RSP, R2		// save original stack pointer
+	MOVD	g, R5
+
+	// Figure out if we need to switch to m->g0 stack.
+	// We get called to create new OS threads too, and those
+	// come in on the m->g0 stack already.
+	MOVD	g_m(g), R6
+	MOVD	m_g0(R6), R6
+	CMP	R6, g
+	BEQ	g0
+	BL	gosave<>(SB)
+	MOVD	R6, g
+	BL	runtime·save_g(SB)
+	MOVD	(g_sched+gobuf_sp)(g), R13
+	MOVD	R13, RSP
+
+	// Now on a scheduling stack (a pthread-created stack).
+g0:
+	// Save room for two of our pointers, plus 32 bytes of callee
+	// save area that lives on the caller stack.
+	MOVD	RSP, R13
+	SUB	$48, R13
+	AND	$~15, R13	// 16-byte alignment for gcc ABI
+	MOVD	R13, RSP
+	MOVD	R5, 40(RSP)	// save old g on stack
+	MOVD	(g_stack+stack_hi)(R5), R5
+	SUB	R2, R5
+	MOVD	R5, 32(RSP)	// save depth in old g stack (can't just save RSP, as stack might be copied during a callback)
+	MOVD	R0, 0(RSP)	// clear back chain pointer (TODO can we give it real back trace information?)
+	// This is a "global call", so put the global entry point in r12
+	MOVD	R3, R12
+	MOVD	R4, R0
+	BL	(R12)
+
+	// Restore g, stack pointer.  R0 is errno, so don't touch it
+	MOVD	40(RSP), g
+	BL	runtime·save_g(SB)
+	MOVD	(g_stack+stack_hi)(g), R5
+	MOVD	32(RSP), R6
+	SUB	R6, R5
+	MOVD	R5, RSP
+	RET
+
+// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+// Turn the fn into a Go func (by taking its address) and call
+// cgocallback_gofunc.
+TEXT runtime·cgocallback(SB),NOSPLIT,$24-24
+	MOVD	$fn+0(FP), R3
+	MOVD	R3, 8(RSP)
+	MOVD	frame+8(FP), R3
+	MOVD	R3, 16(RSP)
+	MOVD	framesize+16(FP), R3
+	MOVD	R3, 24(RSP)
+	MOVD	$runtime·cgocallback_gofunc(SB), R3
+	BL	(R3)
+	RET
+
+// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
+// See cgocall.c for more details.
+TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24
+	NO_LOCAL_POINTERS
+
+	// Load m and g from thread-local storage.
+	MOVB	runtime·iscgo(SB), R3
+	CMP	$0, R3 
+	BEQ	nocgo
+	// TODO(aram):
+	BL runtime·abort(SB)
+nocgo:
+
+	// If g is nil, Go did not create the current thread.
+	// Call needm to obtain one for temporary use.
+	// In this case, we're running on the thread stack, so there's
+	// lots of space, but the linker doesn't know. Hide the call from
+	// the linker analysis by using an indirect call.
+	CMP	$0, g
+	BNE	havem
+	MOVD	g, savedm-8(SP) // g is zero, so is m.
+	MOVD	$runtime·needm(SB), R3
+	BL	(R3)
+
+	// Set m->sched.sp = SP, so that if a panic happens
+	// during the function we are about to execute, it will
+	// have a valid SP to run on the g0 stack.
+	// The next few lines (after the havem label)
+	// will save this SP onto the stack and then write
+	// the same SP back to m->sched.sp. That seems redundant,
+	// but if an unrecovered panic happens, unwindm will
+	// restore the g->sched.sp from the stack location
+	// and then systemstack will try to use it. If we don't set it here,
+	// that restored SP will be uninitialized (typically 0) and
+	// will not be usable.
+	MOVD	g_m(g), R3
+	MOVD	m_g0(R3), R3
+	MOVD	RSP, R0
+	MOVD	R0, (g_sched+gobuf_sp)(R3)
+
+havem:
+	MOVD	g_m(g), R8
+	MOVD	R8, savedm-8(SP)
+	// Now there's a valid m, and we're running on its m->g0.
+	// Save current m->g0->sched.sp on stack and then set it to SP.
+	// Save current sp in m->g0->sched.sp in preparation for
+	// switch back to m->curg stack.
+	// NOTE: unwindm knows that the saved g->sched.sp is at 8(R1) aka savedsp-16(SP).
+	MOVD	m_g0(R8), R3
+	MOVD	(g_sched+gobuf_sp)(R3), R4
+	MOVD	R4, savedsp-16(SP)
+	MOVD	RSP, R0
+	MOVD	R0, (g_sched+gobuf_sp)(R3)
+
+	// Switch to m->curg stack and call runtime.cgocallbackg.
+	// Because we are taking over the execution of m->curg
+	// but *not* resuming what had been running, we need to
+	// save that information (m->curg->sched) so we can restore it.
+	// We can restore m->curg->sched.sp easily, because calling
+	// runtime.cgocallbackg leaves SP unchanged upon return.
+	// To save m->curg->sched.pc, we push it onto the stack.
+	// This has the added benefit that it looks to the traceback
+	// routine like cgocallbackg is going to return to that
+	// PC (because the frame we allocate below has the same
+	// size as cgocallback_gofunc's frame declared above)
+	// so that the traceback will seamlessly trace back into
+	// the earlier calls.
+	//
+	// In the new goroutine, -16(SP) and -8(SP) are unused.
+	MOVD	m_curg(R8), g
+	BL	runtime·save_g(SB)
+	MOVD	(g_sched+gobuf_sp)(g), R4 // prepare stack as R4
+	MOVD	(g_sched+gobuf_pc)(g), R5
+	MOVD	R5, -24(R4)
+	MOVD	$-24(R4), R0
+	MOVD	R0, RSP
+	BL	runtime·cgocallbackg(SB)
+
+	// Restore g->sched (== m->curg->sched) from saved values.
+	MOVD	0(RSP), R5
+	MOVD	R5, (g_sched+gobuf_pc)(g)
+	MOVD	$24(RSP), R4
+	MOVD	R4, (g_sched+gobuf_sp)(g)
+
+	// Switch back to m->g0's stack and restore m->g0->sched.sp.
+	// (Unlike m->curg, the g0 goroutine never uses sched.pc,
+	// so we do not have to restore it.)
+	MOVD	g_m(g), R8
+	MOVD	m_g0(R8), g
+	BL	runtime·save_g(SB)
+	MOVD	(g_sched+gobuf_sp)(g), R0
+	MOVD	R0, RSP
+	MOVD	savedsp-16(SP), R4
+	MOVD	R4, (g_sched+gobuf_sp)(g)
+
+	// If the m on entry was nil, we called needm above to borrow an m
+	// for the duration of the call. Since the call is over, return it with dropm.
+	MOVD	savedm-8(SP), R6
+	CMP	$0, R6
+	BNE	droppedm
+	MOVD	$runtime·dropm(SB), R3
+	BL	(R3)
+droppedm:
+
+	// Done!
+	RET
+
+// void setg(G*); set g. for use by needm.
+TEXT runtime·setg(SB), NOSPLIT, $0-8
+	MOVD	gg+0(FP), g
+	// This only happens if iscgo, so jump straight to save_g
+	BL	runtime·save_g(SB)
+	RET
+
+// save_g saves the g register into pthread-provided
+// thread-local memory, so that we can call externally compiled
+// ppc64 code that will overwrite this register.
+//
+// If !iscgo, this is a no-op.
+TEXT runtime·save_g(SB),NOSPLIT,$-8-0
+	MOVB	runtime·iscgo(SB), R0
+	CMP	$0, R0
+	BEQ	nocgo
+
+	// TODO: implement cgo.
+	BL	runtime·abort(SB)
+
+nocgo:
+	RET
+
+
+TEXT runtime·getcallerpc(SB),NOSPLIT,$-8-16
+	MOVD	0(RSP), R0
+	MOVD	R0, ret+8(FP)
+	RET
+
+TEXT runtime·setcallerpc(SB),NOSPLIT,$-8-16
+	MOVD	pc+8(FP), R0
+	MOVD	R0, 0(RSP)		// set calling pc
+	RET
+
+TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
+	MOVD	argp+0(FP), R0
+	SUB	$8, R0
+	MOVD	R0, ret+8(FP)
+	RET
+
+TEXT runtime·abort(SB),NOSPLIT,$-8-0
+	B	(ZR)
+	UNDEF
+
+// memhash_varlen(p unsafe.Pointer, h seed) uintptr
+// redirects to memhash(p, h, size) using the size
+// stored in the closure.
+TEXT runtime·memhash_varlen(SB),NOSPLIT,$40-24
+	GO_ARGS
+	NO_LOCAL_POINTERS
+	MOVD	p+0(FP), R3
+	MOVD	h+8(FP), R4
+	MOVD	8(R26), R5
+	MOVD	R3, 8(RSP)
+	MOVD	R4, 16(RSP)
+	MOVD	R5, 24(RSP)
+	BL	runtime·memhash(SB)
+	MOVD	32(RSP), R3
+	MOVD	R3, ret+16(FP)
+	RET
+
+TEXT runtime·memeq(SB),NOSPLIT,$-8-25
+	MOVD	a+0(FP), R1
+	MOVD	b+8(FP), R2
+	MOVD	size+16(FP), R3
+	ADD	R1, R3, R6
+	MOVD	$1, R0
+	MOVB	R0, ret+24(FP)
+loop:
+	CMP	R1, R6
+	BEQ	done
+	MOVBU.P	1(R1), R4
+	MOVBU.P	1(R2), R5
+	CMP	R4, R5
+	BEQ	loop
+
+	MOVB	$0, ret+24(FP)
+done:
+	RET
+
+// memequal_varlen(a, b unsafe.Pointer) bool
+TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
+	MOVD	a+0(FP), R3
+	MOVD	b+8(FP), R4
+	CMP	R3, R4
+	BEQ	eq
+	MOVD	8(R26), R5    // compiler stores size at offset 8 in the closure
+	MOVD	R3, 8(RSP)
+	MOVD	R4, 16(RSP)
+	MOVD	R5, 24(RSP)
+	BL	runtime·memeq(SB)
+	MOVBU	32(RSP), R3
+	MOVB	R3, ret+16(FP)
+	RET
+eq:
+	MOVD	$1, R3
+	MOVB	R3, ret+16(FP)
+	RET
+
+// eqstring tests whether two strings are equal.
+// The compiler guarantees that strings passed
+// to eqstring have equal length.
+// See runtime_test.go:eqstring_generic for
+// equivalent Go code.
+TEXT runtime·eqstring(SB),NOSPLIT,$0-33
+	MOVD	s1str+0(FP), R0
+	MOVD	s1len+8(FP), R1
+	MOVD	s2str+16(FP), R2
+	ADD	R0, R1		// end
+loop:
+	CMP	R0, R1
+	BEQ	equal		// reaches the end
+	MOVBU.P	1(R0), R4
+	MOVBU.P	1(R2), R5
+	CMP	R4, R5
+	BEQ	loop
+notequal:
+	MOVB	ZR, ret+32(FP)
+	RET
+equal:
+	MOVD	$1, R0
+	MOVB	R0, ret+32(FP)
+	RET
+
+//
+// functions for other packages
+//
+TEXT bytes·IndexByte(SB),NOSPLIT,$0-40
+	MOVD	b+0(FP), R0
+	MOVD	b_len+8(FP), R1
+	MOVBU	c+24(FP), R2	// byte to find
+	MOVD	R0, R4		// store base for later
+	ADD	R0, R1		// end
+loop:
+	CMP	R0, R1
+	BEQ	notfound
+	MOVBU.P	1(R0), R3
+	CMP	R2, R3
+	BNE	loop
+
+	SUB	$1, R0		// R0 will be one beyond the position we want
+	SUB	R4, R0		// remove base
+	MOVD	R0, ret+32(FP)
+	RET
+
+notfound:
+	MOVD	$-1, R0
+	MOVD	R0, ret+32(FP)
+	RET
+
+TEXT strings·IndexByte(SB),NOSPLIT,$0-32
+	MOVD	s+0(FP), R0
+	MOVD	s_len+8(FP), R1
+	MOVBU	c+16(FP), R2	// byte to find
+	MOVD	R0, R4		// store base for later
+	ADD	R0, R1		// end
+loop:
+	CMP	R0, R1
+	BEQ	notfound
+	MOVBU.P	1(R0), R3
+	CMP	R2, R3
+	BNE	loop
+
+	SUB	$1, R0		// R0 will be one beyond the position we want
+	SUB	R4, R0		// remove base
+	MOVD	R0, ret+24(FP)
+	RET
+
+notfound:
+	MOVD	$-1, R0
+	MOVD	R0, ret+24(FP)
+	RET
+
+// TODO: share code with memeq?
+TEXT bytes·Equal(SB),NOSPLIT,$0-49
+	MOVD	a_len+8(FP), R1
+	MOVD	b_len+32(FP), R3
+	CMP	R1, R3		// unequal lengths are not equal
+	BNE	notequal
+	MOVD	a+0(FP), R0
+	MOVD	b+24(FP), R2
+	ADD	R0, R1		// end
+loop:
+	CMP	R0, R1
+	BEQ	equal		// reaches the end
+	MOVBU.P	1(R0), R4
+	MOVBU.P	1(R2), R5
+	CMP	R4, R5
+	BEQ	loop
+notequal:
+	MOVB	ZR, ret+48(FP)
+	RET
+equal:
+	MOVD	$1, R0
+	MOVB	R0, ret+48(FP)
+	RET
+
+// A Duff's device for zeroing memory.
+// The compiler jumps to computed addresses within
+// this routine to zero chunks of memory.  Do not
+// change this code without also changing the code
+// in ../cmd/7g/ggen.c:/^clearfat.
+// ZR: always zero
+// R16 (aka REGRT1): ptr to memory to be zeroed - 8
+// On return, R16 points to the last zeroed dword.
+TEXT runtime·duffzero(SB), NOSPLIT, $-8-0
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	MOVD.W	ZR, 8(R16)
+	RET
+
+TEXT runtime·fastrand1(SB),NOSPLIT,$-8-4
+	MOVD	g_m(g), R1
+	MOVWU	m_fastrand(R1), R0
+	ADD	R0, R0
+	CMPW	$0, R0
+	BGE	notneg
+	EOR	$0x88888eef, R0
+notneg:
+	MOVW	R0, m_fastrand(R1)
+	MOVW	R0, ret+0(FP)
+	RET
+
+TEXT runtime·return0(SB), NOSPLIT, $0
+	MOVW	$0, R0
+	RET
+
+// The top-most function running on a goroutine
+// returns to goexit+PCQuantum.
+TEXT runtime·goexit(SB),NOSPLIT,$-8-0
+	MOVD	R0, R0	// NOP
+	BL	runtime·goexit1(SB)	// does not return
+
+TEXT runtime·getg(SB),NOSPLIT,$-8-8
+	MOVD	g, ret+0(FP)
+	RET
+
+// TODO(aram): use PRFM here.
+TEXT runtime·prefetcht0(SB),NOSPLIT,$0-8
+	RET
+
+TEXT runtime·prefetcht1(SB),NOSPLIT,$0-8
+	RET
+
+TEXT runtime·prefetcht2(SB),NOSPLIT,$0-8
+	RET
+
+TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8
+	RET
+
diff --git a/src/runtime/atomic_arm64.go b/src/runtime/atomic_arm64.go
new file mode 100644
index 0000000..83ca4dd
--- /dev/null
+++ b/src/runtime/atomic_arm64.go
@@ -0,0 +1,61 @@
+// Copyright 2015 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 runtime
+
+import "unsafe"
+
+//go:noescape
+func xadd(ptr *uint32, delta int32) uint32
+
+//go:noescape
+func xadd64(ptr *uint64, delta int64) uint64
+
+//go:noescape
+func xchg(ptr *uint32, new uint32) uint32
+
+//go:noescape
+func xchg64(ptr *uint64, new uint64) uint64
+
+// NO go:noescape annotation; see atomic_pointer.go.
+func xchgp1(ptr unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer
+
+//go:noescape
+func xchguintptr(ptr *uintptr, new uintptr) uintptr
+
+//go:noescape
+func atomicload(ptr *uint32) uint32
+
+//go:noescape
+func atomicload64(ptr *uint64) uint64
+
+//go:noescape
+func atomicloadp(ptr unsafe.Pointer) unsafe.Pointer
+
+//go:nosplit
+func atomicor8(addr *uint8, v uint8) {
+	// TODO(dfc) implement this in asm.
+	// Align down to 4 bytes and use 32-bit CAS.
+	uaddr := uintptr(unsafe.Pointer(addr))
+	addr32 := (*uint32)(unsafe.Pointer(uaddr &^ 3))
+	word := uint32(v) << ((uaddr & 3) * 8) // little endian
+	for {
+		old := *addr32
+		if cas(addr32, old, old|word) {
+			return
+		}
+	}
+}
+
+//go:noescape
+func cas64(ptr *uint64, old, new uint64) bool
+
+//go:noescape
+func atomicstore(ptr *uint32, val uint32)
+
+//go:noescape
+func atomicstore64(ptr *uint64, val uint64)
+
+// NO go:noescape annotation; see atomic_pointer.go.
+func atomicstorep1(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/atomic_arm64.s b/src/runtime/atomic_arm64.s
new file mode 100644
index 0000000..acd0a62
--- /dev/null
+++ b/src/runtime/atomic_arm64.s
@@ -0,0 +1,113 @@
+// Copyright 2014 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.
+
+#include "textflag.h"
+
+// uint32 runtime·atomicload(uint32 volatile* addr)
+TEXT ·atomicload(SB),NOSPLIT,$-8-12
+	MOVD	ptr+0(FP), R0
+	LDARW	(R0), R0
+	MOVW	R0, ret+8(FP)
+	RET
+
+// uint64 runtime·atomicload64(uint64 volatile* addr)
+TEXT ·atomicload64(SB),NOSPLIT,$-8-16
+	MOVD	ptr+0(FP), R0
+	LDAR	(R0), R0
+	MOVD	R0, ret+8(FP)
+	RET
+
+// void *runtime·atomicloadp(void *volatile *addr)
+TEXT ·atomicloadp(SB),NOSPLIT,$-8-16
+	MOVD	ptr+0(FP), R0
+	LDAR	(R0), R0
+	MOVD	R0, ret+8(FP)
+	RET
+
+TEXT runtime·atomicstorep1(SB), NOSPLIT, $0-16
+	B	runtime·atomicstore64(SB)
+
+TEXT runtime·atomicstore(SB), NOSPLIT, $0-12
+	MOVD	ptr+0(FP), R0
+	MOVW	val+8(FP), R1
+	STLRW	R1, (R0)
+	RET
+
+TEXT runtime·atomicstore64(SB), NOSPLIT, $0-16
+	MOVD	ptr+0(FP), R0
+	MOVD	val+8(FP), R1
+	STLR	R1, (R0)
+	RET
+
+TEXT runtime·xchg(SB), NOSPLIT, $0-20
+again:
+	MOVD	ptr+0(FP), R0
+	MOVW	new+8(FP), R1
+	LDAXRW	(R0), R2
+	STLXRW	R1, (R0), R3
+	CBNZ	R3, again
+	MOVW	R2, ret+16(FP)
+	RET
+
+TEXT runtime·xchg64(SB), NOSPLIT, $0-24
+again:
+	MOVD	ptr+0(FP), R0
+	MOVD	new+8(FP), R1
+	LDAXR	(R0), R2
+	STLXR	R1, (R0), R3
+	CBNZ	R3, again
+	MOVD	R2, ret+16(FP)
+	RET
+
+// bool runtime·cas64(uint64 *ptr, uint64 old, uint64 new)
+// Atomically:
+//      if(*val == *old){
+//              *val = new;
+//              return 1;
+//      } else {
+//              return 0;
+//      }
+TEXT runtime·cas64(SB), NOSPLIT, $0-25
+	MOVD	ptr+0(FP), R0
+	MOVD	old+8(FP), R1
+	MOVD	new+16(FP), R2
+again:
+	LDAXR	(R0), R3
+	CMP	R1, R3
+	BNE	ok
+	STLXR	R2, (R0), R3
+	CBNZ	R3, again
+ok:
+	CSET	EQ, R0
+	MOVB	R0, ret+24(FP)
+	RET
+
+// uint32 xadd(uint32 volatile *ptr, int32 delta)
+// Atomically:
+//      *val += delta;
+//      return *val;
+TEXT runtime·xadd(SB), NOSPLIT, $0-20
+again:
+	MOVD	ptr+0(FP), R0
+	MOVW	delta+8(FP), R1
+	LDAXRW	(R0), R2
+	ADDW	R2, R1, R2
+	STLXRW	R2, (R0), R3
+	CBNZ	R3, again
+	MOVW	R2, ret+16(FP)
+	RET
+
+TEXT runtime·xadd64(SB), NOSPLIT, $0-24
+again:
+	MOVD	ptr+0(FP), R0
+	MOVD	delta+8(FP), R1
+	LDAXR	(R0), R2
+	ADD	R2, R1, R2
+	STLXR	R2, (R0), R3
+	CBNZ	R3, again
+	MOVD	R2, ret+16(FP)
+	RET
+
+TEXT runtime·xchguintptr(SB), NOSPLIT, $0-24
+	B	runtime·xchg64(SB)
diff --git a/src/runtime/cputicks.go b/src/runtime/cputicks.go
index e0593d5..162e026 100644
--- a/src/runtime/cputicks.go
+++ b/src/runtime/cputicks.go
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // +build !arm
+// +build !arm64
 
 package runtime
 
diff --git a/src/runtime/debug/stubs.s b/src/runtime/debug/stubs.s
index b117063..9dc8e54 100644
--- a/src/runtime/debug/stubs.s
+++ b/src/runtime/debug/stubs.s
@@ -7,6 +7,9 @@
 #ifdef GOARCH_arm
 #define JMP B
 #endif
+#ifdef GOARCH_arm64
+#define JMP B
+#endif
 #ifdef GOARCH_ppc64
 #define JMP BR
 #endif
diff --git a/src/runtime/defs_linux_arm64.go b/src/runtime/defs_linux_arm64.go
new file mode 100644
index 0000000..1a4d884
--- /dev/null
+++ b/src/runtime/defs_linux_arm64.go
@@ -0,0 +1,178 @@
+// Created by cgo -cdefs and converted (by hand) to Go
+// ../cmd/cgo/cgo -cdefs defs_linux.go defs1_linux.go defs2_linux.go
+
+package runtime
+
+const (
+	_EINTR  = 0x4
+	_EAGAIN = 0xb
+	_ENOMEM = 0xc
+
+	_PROT_NONE  = 0x0
+	_PROT_READ  = 0x1
+	_PROT_WRITE = 0x2
+	_PROT_EXEC  = 0x4
+
+	_MAP_ANON    = 0x20
+	_MAP_PRIVATE = 0x2
+	_MAP_FIXED   = 0x10
+
+	_MADV_DONTNEED   = 0x4
+	_MADV_HUGEPAGE   = 0xe
+	_MADV_NOHUGEPAGE = 0xf
+
+	_SA_RESTART  = 0x10000000
+	_SA_ONSTACK  = 0x8000000
+	_SA_RESTORER = 0x0 // Only used on intel
+	_SA_SIGINFO  = 0x4
+
+	_SIGHUP    = 0x1
+	_SIGINT    = 0x2
+	_SIGQUIT   = 0x3
+	_SIGILL    = 0x4
+	_SIGTRAP   = 0x5
+	_SIGABRT   = 0x6
+	_SIGBUS    = 0x7
+	_SIGFPE    = 0x8
+	_SIGKILL   = 0x9
+	_SIGUSR1   = 0xa
+	_SIGSEGV   = 0xb
+	_SIGUSR2   = 0xc
+	_SIGPIPE   = 0xd
+	_SIGALRM   = 0xe
+	_SIGSTKFLT = 0x10
+	_SIGCHLD   = 0x11
+	_SIGCONT   = 0x12
+	_SIGSTOP   = 0x13
+	_SIGTSTP   = 0x14
+	_SIGTTIN   = 0x15
+	_SIGTTOU   = 0x16
+	_SIGURG    = 0x17
+	_SIGXCPU   = 0x18
+	_SIGXFSZ   = 0x19
+	_SIGVTALRM = 0x1a
+	_SIGPROF   = 0x1b
+	_SIGWINCH  = 0x1c
+	_SIGIO     = 0x1d
+	_SIGPWR    = 0x1e
+	_SIGSYS    = 0x1f
+
+	_FPE_INTDIV = 0x1
+	_FPE_INTOVF = 0x2
+	_FPE_FLTDIV = 0x3
+	_FPE_FLTOVF = 0x4
+	_FPE_FLTUND = 0x5
+	_FPE_FLTRES = 0x6
+	_FPE_FLTINV = 0x7
+	_FPE_FLTSUB = 0x8
+
+	_BUS_ADRALN = 0x1
+	_BUS_ADRERR = 0x2
+	_BUS_OBJERR = 0x3
+
+	_SEGV_MAPERR = 0x1
+	_SEGV_ACCERR = 0x2
+
+	_ITIMER_REAL    = 0x0
+	_ITIMER_VIRTUAL = 0x1
+	_ITIMER_PROF    = 0x2
+
+	_EPOLLIN       = 0x1
+	_EPOLLOUT      = 0x4
+	_EPOLLERR      = 0x8
+	_EPOLLHUP      = 0x10
+	_EPOLLRDHUP    = 0x2000
+	_EPOLLET       = 0x80000000
+	_EPOLL_CLOEXEC = 0x80000
+	_EPOLL_CTL_ADD = 0x1
+	_EPOLL_CTL_DEL = 0x2
+	_EPOLL_CTL_MOD = 0x3
+)
+
+type timespec struct {
+	tv_sec  int64
+	tv_nsec int64
+}
+
+func (ts *timespec) set_sec(x int64) {
+	ts.tv_sec = x
+}
+
+func (ts *timespec) set_nsec(x int32) {
+	ts.tv_nsec = int64(x)
+}
+
+type timeval struct {
+	tv_sec  int64
+	tv_usec int64
+}
+
+func (tv *timeval) set_usec(x int32) {
+	tv.tv_usec = int64(x)
+}
+
+type sigactiont struct {
+	sa_handler  uintptr
+	sa_flags    uint64
+	sa_restorer uintptr
+	sa_mask     uint64
+}
+
+type siginfo struct {
+	si_signo int32
+	si_errno int32
+	si_code  int32
+	// below here is a union; si_addr is the only field we use
+	si_addr uint64
+}
+
+type itimerval struct {
+	it_interval timeval
+	it_value    timeval
+}
+
+type epollevent struct {
+	events uint32
+	_pad   uint32
+	data   [8]byte // to match amd64
+}
+
+// Created by cgo -cdefs and then converted to Go by hand
+// ../cmd/cgo/cgo -cdefs defs_linux.go defs1_linux.go defs2_linux.go
+
+const (
+	_O_RDONLY  = 0x0
+	_O_CLOEXEC = 0x80000
+)
+
+type usigset struct {
+	__val [16]uint64
+}
+
+type sigaltstackt struct {
+	ss_sp     *byte
+	ss_flags  int32
+	pad_cgo_0 [4]byte
+	ss_size   uintptr
+}
+
+type sigcontext struct {
+	fault_address uint64
+	/* AArch64 registers */
+	regs       [31]uint64
+	sp         uint64
+	pc         uint64
+	pstate     uint64
+	_pad       [8]byte // __attribute__((__aligned__(16)))
+	__reserved [4096]byte
+}
+
+type ucontext struct {
+	uc_flags    uint64
+	uc_link     *ucontext
+	uc_stack    sigaltstackt
+	uc_sigmask  uint64
+	_pad        [(1024 - 64) / 8]byte
+	_pad2       [8]byte // sigcontext must be aligned to 16-byte
+	uc_mcontext sigcontext
+}
diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go
index 6d6b5e4..f353a4e 100644
--- a/src/runtime/gcinfo_test.go
+++ b/src/runtime/gcinfo_test.go
@@ -134,7 +134,7 @@
 			typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64
 			typePointer, typeDead, // i string
 		}
-	case "amd64", "ppc64", "ppc64le":
+	case "arm64", "amd64", "ppc64", "ppc64le":
 		return []byte{
 			typePointer,                        // q *int
 			typeScalar, typeScalar, typeScalar, // w byte; e [17]byte
diff --git a/src/runtime/hash64.go b/src/runtime/hash64.go
index d10b781..716db61 100644
--- a/src/runtime/hash64.go
+++ b/src/runtime/hash64.go
@@ -6,7 +6,7 @@
 //   xxhash: https://code.google.com/p/xxhash/
 // cityhash: https://code.google.com/p/cityhash/
 
-// +build amd64 amd64p32 ppc64 ppc64le
+// +build amd64 amd64p32 arm64 ppc64 ppc64le
 
 package runtime
 
diff --git a/src/runtime/lfstack_linux_arm64.go b/src/runtime/lfstack_linux_arm64.go
new file mode 100644
index 0000000..54cae39
--- /dev/null
+++ b/src/runtime/lfstack_linux_arm64.go
@@ -0,0 +1,25 @@
+// Copyright 2014 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 runtime
+
+import "unsafe"
+
+// In addition to the 16 bits taken from the top, we can take 3 from the
+// bottom, because node must be pointer-aligned, giving a total of 19 bits
+// of count.
+const (
+	addrBits = 48
+	cntBits  = 64 - addrBits + 3
+)
+
+func lfstackPack(node *lfnode, cnt uintptr) uint64 {
+	return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<<cntBits-1))
+}
+
+func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) {
+	node = (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3)))
+	cnt = uintptr(val & (1<<cntBits - 1))
+	return
+}
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index c334562..0b7b89a 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -253,12 +253,20 @@
 		// but it hardly matters: e0 00 is not valid UTF-8 either.
 		//
 		// If this fails we fall back to the 32 bit memory mechanism
+		//
+		// However, on arm64, we ignore all this advice above and slam the
+		// allocation at 0x40 << 32 because when using 4k pages with 3-level
+		// translation buffers, the user address space is limited to 39 bits
 		arenaSize := round(_MaxMem, _PageSize)
 		bitmapSize = arenaSize / (ptrSize * 8 / 4)
 		spansSize = arenaSize / _PageSize * ptrSize
 		spansSize = round(spansSize, _PageSize)
 		for i := 0; i <= 0x7f; i++ {
-			p = uintptr(i)<<40 | uintptrMask&(0x00c0<<32)
+			if GOARCH == "arm64" {
+				p = uintptr(i)<<40 | uintptrMask&(0x0040<<32)
+			} else {
+				p = uintptr(i)<<40 | uintptrMask&(0x00c0<<32)
+			}
 			pSize = bitmapSize + spansSize + arenaSize + _PageSize
 			p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved))
 			if p != 0 {
diff --git a/src/runtime/memclr_arm64.s b/src/runtime/memclr_arm64.s
new file mode 100644
index 0000000..c44c123
--- /dev/null
+++ b/src/runtime/memclr_arm64.s
@@ -0,0 +1,18 @@
+// Copyright 2014 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.
+
+#include "textflag.h"
+
+// void runtime·memclr(void*, uintptr)
+TEXT runtime·memclr(SB),NOSPLIT,$0-16
+	MOVD	ptr+0(FP), R3
+	MOVD	n+8(FP), R4
+	CMP	$0, R4
+	BEQ	done
+	ADD	R3, R4, R4
+	MOVBU.P	$0, 1(R3)
+	CMP	R3, R4
+	BNE	-2(PC)
+done:
+	RET
diff --git a/src/runtime/memmove_arm64.s b/src/runtime/memmove_arm64.s
new file mode 100644
index 0000000..66059a7
--- /dev/null
+++ b/src/runtime/memmove_arm64.s
@@ -0,0 +1,36 @@
+// Copyright 2014 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.
+
+#include "textflag.h"
+
+// void runtime·memmove(void*, void*, uintptr)
+TEXT runtime·memmove(SB), NOSPLIT, $-8-24
+	MOVD	to+0(FP), R3
+	MOVD	from+8(FP), R4
+	MOVD	n+16(FP), R5
+	CMP	$0, R5
+	BNE	check
+	RET
+
+check:
+	CMP	R3, R4
+	BLT	backward
+
+	ADD	R3, R5
+loop:
+	MOVBU.P	1(R4), R6
+	MOVBU.P	R6, 1(R3)
+	CMP	R3, R5
+	BNE	loop
+	RET
+
+backward:
+	ADD	R5, R4
+	ADD	R3, R5
+loop1:
+	MOVBU.W	-1(R4), R6
+	MOVBU.W	R6, -1(R5)
+	CMP	R3, R5
+	BNE	loop1
+	RET
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index b17be92..ec189ef 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -289,10 +289,13 @@
 	// Scan local variables if stack frame has been allocated.
 	size := frame.varp - frame.sp
 	var minsize uintptr
-	if thechar != '6' && thechar != '8' {
-		minsize = ptrSize
-	} else {
+	switch thechar {
+	case '6', '8':
 		minsize = 0
+	case '7':
+		minsize = spAlign
+	default:
+		minsize = ptrSize
 	}
 	if size > minsize {
 		stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
diff --git a/src/runtime/noasm.go b/src/runtime/noasm.go
index 7ffde37..4b3c577 100644
--- a/src/runtime/noasm.go
+++ b/src/runtime/noasm.go
@@ -4,7 +4,7 @@
 
 // Routines that are implemented in assembly in asm_{amd64,386}.s
 
-// +build arm ppc64 ppc64le
+// +build arm arm64 ppc64 ppc64le
 
 package runtime
 
diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go
new file mode 100644
index 0000000..c3ad871
--- /dev/null
+++ b/src/runtime/os_linux_arm64.go
@@ -0,0 +1,19 @@
+// Copyright 2015 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 runtime
+
+const (
+	_AT_NULL   = 0
+	_AT_RANDOM = 25 // introduced in 2.6.29
+)
+
+var randomNumber uint32
+
+func cputicks() int64 {
+	// Currently cputicks() is used in blocking profiler and to seed fastrand1().
+	// nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
+	// randomNumber provides better seeding of fastrand1.
+	return nanotime() + int64(randomNumber)
+}
diff --git a/src/runtime/panic1.go b/src/runtime/panic1.go
index 4c0eb40..c14cf27 100644
--- a/src/runtime/panic1.go
+++ b/src/runtime/panic1.go
@@ -10,7 +10,7 @@
 //uint32 runtime·panicking;
 var paniclk mutex
 
-const hasLinkRegister = GOARCH == "arm" || GOARCH == "ppc64" || GOARCH == "ppc64le"
+const hasLinkRegister = GOARCH == "arm" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le"
 
 // Unwind the stack after a deferred function calls recover
 // after a panic.  Then arrange to continue running as though
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 3639046..5e5d0ef 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -2042,15 +2042,19 @@
 		throw("newproc1: new g is not Gdead")
 	}
 
-	sp := newg.stack.hi
-	sp -= 4 * regSize // extra space in case of reads slightly beyond frame
-	sp -= uintptr(siz)
-	memmove(unsafe.Pointer(sp), unsafe.Pointer(argp), uintptr(narg))
+	totalSize := 4*regSize + uintptr(siz) // extra space in case of reads slightly beyond frame
+	if hasLinkRegister {
+		totalSize += ptrSize
+	}
+	totalSize += -totalSize & (spAlign - 1) // align to spAlign
+	sp := newg.stack.hi - totalSize
+	spArg := sp
 	if hasLinkRegister {
 		// caller's LR
-		sp -= ptrSize
 		*(*unsafe.Pointer)(unsafe.Pointer(sp)) = nil
+		spArg += ptrSize
 	}
+	memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
 
 	memclr(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
 	newg.sched.sp = sp
diff --git a/src/runtime/rt0_linux_arm64.s b/src/runtime/rt0_linux_arm64.s
new file mode 100644
index 0000000..1eb0352
--- /dev/null
+++ b/src/runtime/rt0_linux_arm64.s
@@ -0,0 +1,19 @@
+// Copyright 2015 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.
+
+#include "textflag.h"
+
+TEXT _rt0_arm64_linux(SB),NOSPLIT,$-8
+	MOVD	0(RSP), R0	// argc
+	ADD	$8, RSP, R1	// argv
+	BL	main(SB)
+
+TEXT main(SB),NOSPLIT,$-8
+	MOVD	$runtime·rt0_go(SB), R2
+	BL	(R2)
+exit:
+	MOVD $0, R0
+	MOVD	$94, R8	// sys_exit
+	SVC
+	B	exit
diff --git a/src/runtime/signal_arm64.go b/src/runtime/signal_arm64.go
new file mode 100644
index 0000000..cc89858
--- /dev/null
+++ b/src/runtime/signal_arm64.go
@@ -0,0 +1,139 @@
+// Copyright 2014 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.
+
+// +build linux
+
+package runtime
+
+import "unsafe"
+
+func dumpregs(c *sigctxt) {
+	print("r0      ", hex(c.r0()), "\n")
+	print("r1      ", hex(c.r1()), "\n")
+	print("r2      ", hex(c.r2()), "\n")
+	print("r3      ", hex(c.r3()), "\n")
+	print("r4      ", hex(c.r4()), "\n")
+	print("r5      ", hex(c.r5()), "\n")
+	print("r6      ", hex(c.r6()), "\n")
+	print("r7      ", hex(c.r7()), "\n")
+	print("r8      ", hex(c.r8()), "\n")
+	print("r9      ", hex(c.r9()), "\n")
+	print("r10     ", hex(c.r10()), "\n")
+	print("r11     ", hex(c.r11()), "\n")
+	print("r12     ", hex(c.r12()), "\n")
+	print("r13     ", hex(c.r13()), "\n")
+	print("r14     ", hex(c.r14()), "\n")
+	print("r15     ", hex(c.r15()), "\n")
+	print("r16     ", hex(c.r16()), "\n")
+	print("r17     ", hex(c.r17()), "\n")
+	print("r18     ", hex(c.r18()), "\n")
+	print("r19     ", hex(c.r19()), "\n")
+	print("r20     ", hex(c.r20()), "\n")
+	print("r21     ", hex(c.r21()), "\n")
+	print("r22     ", hex(c.r22()), "\n")
+	print("r23     ", hex(c.r23()), "\n")
+	print("r24     ", hex(c.r24()), "\n")
+	print("r25     ", hex(c.r25()), "\n")
+	print("r26     ", hex(c.r26()), "\n")
+	print("r27     ", hex(c.r27()), "\n")
+	print("r28     ", hex(c.r28()), "\n")
+	print("r29     ", hex(c.r29()), "\n")
+	print("lr      ", hex(c.lr()), "\n")
+	print("sp      ", hex(c.sp()), "\n")
+	print("pc      ", hex(c.pc()), "\n")
+	print("fault   ", hex(c.fault()), "\n")
+}
+
+func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
+	_g_ := getg()
+	c := &sigctxt{info, ctxt}
+
+	if sig == _SIGPROF {
+		sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp, _g_.m)
+		return
+	}
+
+	flags := int32(_SigThrow)
+	if sig < uint32(len(sigtable)) {
+		flags = sigtable[sig].flags
+	}
+	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
+		// Make it look like a call to the signal func.
+		// Have to pass arguments out of band since
+		// augmenting the stack frame would break
+		// the unwinding code.
+		gp.sig = sig
+		gp.sigcode0 = uintptr(c.sigcode())
+		gp.sigcode1 = uintptr(c.fault())
+		gp.sigpc = uintptr(c.pc())
+
+		// We arrange lr, and pc to pretend the panicking
+		// function calls sigpanic directly.
+		// Always save LR to stack so that panics in leaf
+		// functions are correctly handled. This smashes
+		// the stack frame but we're not going back there
+		// anyway.
+		sp := c.sp() - spAlign // needs only sizeof uint64, but must align the stack
+		c.set_sp(sp)
+		*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.lr()
+
+		// Don't bother saving PC if it's zero, which is
+		// probably a call to a nil func: the old link register
+		// is more useful in the stack trace.
+		if gp.sigpc != 0 {
+			c.set_lr(uint64(gp.sigpc))
+		}
+
+		// In case we are panicking from external C code
+		c.set_r28(uint64(uintptr(unsafe.Pointer(gp))))
+		c.set_pc(uint64(funcPC(sigpanic)))
+		return
+	}
+
+	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
+		if sigsend(sig) {
+			return
+		}
+	}
+
+	if flags&_SigKill != 0 {
+		exit(2)
+	}
+
+	if flags&_SigThrow == 0 {
+		return
+	}
+
+	_g_.m.throwing = 1
+	_g_.m.caughtsig = gp
+	startpanic()
+
+	if sig < uint32(len(sigtable)) {
+		print(sigtable[sig].name, "\n")
+	} else {
+		print("Signal ", sig, "\n")
+	}
+
+	print("PC=", hex(c.pc()), "\n")
+	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
+		print("signal arrived during cgo execution\n")
+		gp = _g_.m.lockedg
+	}
+	print("\n")
+
+	var docrash bool
+	if gotraceback(&docrash) > 0 {
+		goroutineheader(gp)
+		tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp)
+		tracebackothers(gp)
+		print("\n")
+		dumpregs(c)
+	}
+
+	if docrash {
+		crash()
+	}
+
+	exit(2)
+}
diff --git a/src/runtime/signal_linux_arm64.go b/src/runtime/signal_linux_arm64.go
new file mode 100644
index 0000000..7d8b0104
--- /dev/null
+++ b/src/runtime/signal_linux_arm64.go
@@ -0,0 +1,61 @@
+// Copyright 2015 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 runtime
+
+import "unsafe"
+
+type sigctxt struct {
+	info *siginfo
+	ctxt unsafe.Pointer
+}
+
+func (c *sigctxt) regs() *sigcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
+func (c *sigctxt) r0() uint64        { return c.regs().regs[0] }
+func (c *sigctxt) r1() uint64        { return c.regs().regs[1] }
+func (c *sigctxt) r2() uint64        { return c.regs().regs[2] }
+func (c *sigctxt) r3() uint64        { return c.regs().regs[3] }
+func (c *sigctxt) r4() uint64        { return c.regs().regs[4] }
+func (c *sigctxt) r5() uint64        { return c.regs().regs[5] }
+func (c *sigctxt) r6() uint64        { return c.regs().regs[6] }
+func (c *sigctxt) r7() uint64        { return c.regs().regs[7] }
+func (c *sigctxt) r8() uint64        { return c.regs().regs[8] }
+func (c *sigctxt) r9() uint64        { return c.regs().regs[9] }
+func (c *sigctxt) r10() uint64       { return c.regs().regs[10] }
+func (c *sigctxt) r11() uint64       { return c.regs().regs[11] }
+func (c *sigctxt) r12() uint64       { return c.regs().regs[12] }
+func (c *sigctxt) r13() uint64       { return c.regs().regs[13] }
+func (c *sigctxt) r14() uint64       { return c.regs().regs[14] }
+func (c *sigctxt) r15() uint64       { return c.regs().regs[15] }
+func (c *sigctxt) r16() uint64       { return c.regs().regs[16] }
+func (c *sigctxt) r17() uint64       { return c.regs().regs[17] }
+func (c *sigctxt) r18() uint64       { return c.regs().regs[18] }
+func (c *sigctxt) r19() uint64       { return c.regs().regs[19] }
+func (c *sigctxt) r20() uint64       { return c.regs().regs[20] }
+func (c *sigctxt) r21() uint64       { return c.regs().regs[21] }
+func (c *sigctxt) r22() uint64       { return c.regs().regs[22] }
+func (c *sigctxt) r23() uint64       { return c.regs().regs[23] }
+func (c *sigctxt) r24() uint64       { return c.regs().regs[24] }
+func (c *sigctxt) r25() uint64       { return c.regs().regs[25] }
+func (c *sigctxt) r26() uint64       { return c.regs().regs[26] }
+func (c *sigctxt) r27() uint64       { return c.regs().regs[27] }
+func (c *sigctxt) r28() uint64       { return c.regs().regs[28] }
+func (c *sigctxt) r29() uint64       { return c.regs().regs[29] }
+func (c *sigctxt) lr() uint64        { return c.regs().regs[30] }
+func (c *sigctxt) sp() uint64        { return c.regs().sp }
+func (c *sigctxt) pc() uint64        { return c.regs().pc }
+func (c *sigctxt) pstate() uint64    { return c.regs().pstate }
+func (c *sigctxt) fault() uint64     { return c.regs().fault_address }
+
+func (c *sigctxt) sigcode() uint64 { return uint64(c.info.si_code) }
+func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr }
+
+func (c *sigctxt) set_pc(x uint64)  { c.regs().pc = x }
+func (c *sigctxt) set_sp(x uint64)  { c.regs().sp = x }
+func (c *sigctxt) set_lr(x uint64)  { c.regs().regs[30] = x }
+func (c *sigctxt) set_r28(x uint64) { c.regs().regs[28] = x }
+
+func (c *sigctxt) set_sigaddr(x uint64) {
+	*(*uintptr)(add(unsafe.Pointer(c.info), 2*ptrSize)) = uintptr(x)
+}
diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go
index 3f89bb1..5f28d28 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -439,10 +439,13 @@
 	// Adjust local variables if stack frame has been allocated.
 	size := frame.varp - frame.sp
 	var minsize uintptr
-	if thechar != '6' && thechar != '8' {
-		minsize = ptrSize
-	} else {
+	switch thechar {
+	case '6', '8':
 		minsize = 0
+	case '7':
+		minsize = spAlign
+	default:
+		minsize = ptrSize
 	}
 	if size > minsize {
 		var bv bitvector
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index 00f74f8..99d8dd4 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -8,8 +8,9 @@
 
 // Declarations for runtime services implemented in C or assembly.
 
-const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
-const regSize = 4 << (^uintreg(0) >> 63) // unsafe.Sizeof(uintreg(0)) but an ideal const
+const ptrSize = 4 << (^uintptr(0) >> 63)             // unsafe.Sizeof(uintptr(0)) but an ideal const
+const regSize = 4 << (^uintreg(0) >> 63)             // unsafe.Sizeof(uintreg(0)) but an ideal const
+const spAlign = 1*(1-goarch_arm64) + 16*goarch_arm64 // SP alignment: 1 normally, 16 for ARM64
 
 // Should be a built-in for unsafe.Pointer?
 //go:nosplit
diff --git a/src/runtime/sys_arm64.go b/src/runtime/sys_arm64.go
new file mode 100644
index 0000000..dee23ef
--- /dev/null
+++ b/src/runtime/sys_arm64.go
@@ -0,0 +1,36 @@
+// Copyright 2013 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 runtime
+
+import "unsafe"
+
+// adjust Gobuf as if it executed a call to fn with context ctxt
+// and then did an immediate Gosave.
+func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
+	if buf.lr != 0 {
+		throw("invalid use of gostartcall")
+	}
+	buf.lr = buf.pc
+	buf.pc = uintptr(fn)
+	buf.ctxt = ctxt
+}
+
+// Called to rewind context saved during morestack back to beginning of function.
+// To help us, the linker emits a jmp back to the beginning right after the
+// call to morestack. We just have to decode and apply that jump.
+func rewindmorestack(buf *gobuf) {
+	var inst uint32
+	if buf.pc&3 == 0 && buf.pc != 0 {
+		inst = *(*uint32)(unsafe.Pointer(buf.pc))
+		// section C3.2.6 Unconditional branch (immediate)
+		if inst>>26 == 0x05 {
+			buf.pc += uintptr(int32(inst<<6) >> 4)
+			return
+		}
+	}
+
+	print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
+	throw("runtime: misuse of rewindmorestack")
+}
diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s
new file mode 100644
index 0000000..0d0131b8
--- /dev/null
+++ b/src/runtime/sys_linux_arm64.s
@@ -0,0 +1,431 @@
+// Copyright 2014 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.
+
+//
+// System calls and other sys.stuff for arm64, Linux
+//
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "textflag.h"
+
+#define AT_FDCWD -100
+
+#define SYS_exit		93
+#define SYS_read		63
+#define SYS_write		64
+#define SYS_openat		56
+#define SYS_close		57
+#define SYS_fcntl		25
+#define SYS_gettimeofday	169
+#define SYS_pselect6		72
+#define SYS_mmap		222
+#define SYS_munmap		215
+#define SYS_setitimer		103
+#define SYS_clone		220
+#define SYS_sched_yield		124
+#define SYS_rt_sigreturn	139
+#define SYS_rt_sigaction	134
+#define SYS_rt_sigprocmask	135
+#define SYS_sigaltstack		132
+#define SYS_getrlimit		163
+#define SYS_madvise		233
+#define SYS_mincore		232
+#define SYS_gettid		178
+#define SYS_tkill		130
+#define SYS_futex		98
+#define SYS_sched_getaffinity	123
+#define SYS_exit_group		94
+#define SYS_epoll_create1	20
+#define SYS_epoll_ctl		21
+#define SYS_epoll_pwait		22
+#define SYS_clock_gettime	113
+
+TEXT runtime·exit(SB),NOSPLIT,$-8-4
+	MOVW	code+0(FP), R0
+	MOVD	$SYS_exit_group, R8
+	SVC
+	RET
+
+TEXT runtime·exit1(SB),NOSPLIT,$-8-4
+	MOVW	code+0(FP), R0
+	MOVD	$SYS_exit, R8
+	SVC
+	RET
+
+TEXT runtime·open(SB),NOSPLIT,$-8-20
+	MOVD	$AT_FDCWD, R0
+	MOVD	name+0(FP), R1
+	MOVW	mode+8(FP), R2
+	MOVW	perm+12(FP), R3
+	MOVD	$SYS_openat, R8
+	SVC
+	CMN	$4095, R0
+	BCC	done
+	MOVW	$-1, R0
+done:
+	MOVW	R0, ret+16(FP)
+	RET
+
+TEXT runtime·close(SB),NOSPLIT,$-8-12
+	MOVW	fd+0(FP), R0
+	MOVD	$SYS_close, R8
+	SVC
+	BCC	done
+	MOVW	$-1, R0
+done:
+	MOVW	R0, ret+8(FP)
+	RET
+
+TEXT runtime·write(SB),NOSPLIT,$-8-28
+	MOVD	fd+0(FP), R0
+	MOVD	p+8(FP), R1
+	MOVW	n+16(FP), R2
+	MOVD	$SYS_write, R8
+	SVC
+	BCC	done
+	MOVW	$-1, R0
+done:
+	MOVW	R0, ret+24(FP)
+	RET
+
+TEXT runtime·read(SB),NOSPLIT,$-8-28
+	MOVW	fd+0(FP), R0
+	MOVD	p+8(FP), R1
+	MOVW	n+16(FP), R2
+	MOVD	$SYS_read, R8
+	SVC
+	BCC	done
+	MOVW	$-1, R0
+done:
+	MOVW	R0, ret+24(FP)
+	RET
+
+TEXT runtime·getrlimit(SB),NOSPLIT,$-8-20
+	MOVW	kind+0(FP), R0
+	MOVD	limit+8(FP), R1
+	MOVD	$SYS_getrlimit, R8
+	SVC
+	MOVW	R0, ret+16(FP)
+	RET
+
+TEXT runtime·usleep(SB),NOSPLIT,$16-4
+	MOVWU	usec+0(FP), R3
+	MOVD	R3, R5
+	MOVW	$1000000, R4
+	UDIV	R4, R3
+	MOVD	R3, 8(RSP)
+	MUL	R3, R4
+	SUB	R4, R5
+	MOVW	$1000, R4
+	MUL	R4, R5
+	MOVD	R5, 16(RSP)
+
+	// pselect6(0, 0, 0, 0, &ts, 0)
+	MOVD	$0, R0
+	MOVD	R0, R1
+	MOVD	R0, R2
+	MOVD	R0, R3
+	ADD	$8, RSP, R4
+	MOVD	R0, R5
+	MOVD	$SYS_pselect6, R8
+	SVC
+	RET
+
+TEXT runtime·raise(SB),NOSPLIT,$-8
+	MOVD	$SYS_gettid, R8
+	SVC
+	MOVW	R0, R0	// arg 1 tid
+	MOVW	sig+0(FP), R1	// arg 2
+	MOVD	$SYS_tkill, R8
+	SVC
+	RET
+
+TEXT runtime·setitimer(SB),NOSPLIT,$-8-24
+	MOVW	mode+0(FP), R0
+	MOVD	new+8(FP), R1
+	MOVD	old+16(FP), R2
+	MOVD	$SYS_setitimer, R8
+	SVC
+	RET
+
+TEXT runtime·mincore(SB),NOSPLIT,$-8-28
+	MOVD	addr+0(FP), R0
+	MOVD	n+8(FP), R1
+	MOVD	dst+16(FP), R2
+	MOVD	$SYS_mincore, R8
+	SVC
+	MOVW	R0, ret+24(FP)
+	RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$16-12
+	MOVD	RSP, R0
+	MOVD	$0, R1
+	MOVD	$SYS_gettimeofday, R8
+	SVC
+	MOVD	0(RSP), R3	// sec
+	MOVD	8(RSP), R5	// usec
+	MOVD	$1000, R4
+	MUL	R4, R5
+	MOVD	R3, sec+0(FP)
+	MOVW	R5, nsec+8(FP)
+	RET
+
+TEXT runtime·nanotime(SB),NOSPLIT,$16-8
+	MOVW	$1, R0 // CLOCK_MONOTONIC
+	MOVD	RSP, R1
+	MOVD	$SYS_clock_gettime, R8
+	SVC
+	MOVD	0(RSP), R3	// sec
+	MOVD	8(RSP), R5	// nsec
+	// sec is in R3, nsec in R5
+	// return nsec in R3
+	MOVD	$1000000000, R4
+	MUL	R4, R3
+	ADD	R5, R3
+	MOVD	R3, ret+0(FP)
+	RET
+
+TEXT runtime·rtsigprocmask(SB),NOSPLIT,$-8-28
+	MOVW	sig+0(FP), R0
+	MOVD	new+8(FP), R1
+	MOVD	old+16(FP), R2
+	MOVW	size+24(FP), R3
+	MOVD	$SYS_rt_sigprocmask, R8
+	SVC
+	CMN	$4095, R0
+	BCC	done
+	MOVD	$0, R0
+	MOVD	R0, (R0)	// crash
+done:
+	RET
+
+TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36
+	MOVD	sig+0(FP), R0
+	MOVD	new+8(FP), R1
+	MOVD	old+16(FP), R2
+	MOVD	size+24(FP), R3
+	MOVD	$SYS_rt_sigaction, R8
+	SVC
+	MOVW	R0, ret+32(FP)
+	RET
+
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
+	// this might be called in external code context,
+	// where g is not set.
+	// first save R0, because runtime·load_g will clobber it
+	MOVW	R0, 8(RSP)
+	// TODO(minux): iscgo & load_g
+
+	// check that g exists
+	CMP	g, ZR
+	BNE	ok
+	MOVD	$runtime·badsignal(SB), R0
+	BL	(R0)
+	RET
+
+ok:
+	// save g
+	MOVD	g, 40(RSP)
+	MOVD	g, R6
+
+	// g = m->gsignal
+	MOVD	g_m(g), R7
+	MOVD	m_gsignal(R7), g
+
+	// R0 is already saved above
+	MOVD	R1, 16(RSP)
+	MOVD	R2, 24(RSP)
+	MOVD	R6, 32(RSP)
+
+	BL	runtime·sighandler(SB)
+
+	// restore g
+	MOVD	40(RSP), g
+	RET
+
+TEXT runtime·mmap(SB),NOSPLIT,$-8
+	MOVD	addr+0(FP), R0
+	MOVD	n+8(FP), R1
+	MOVW	prot+16(FP), R2
+	MOVW	flags+20(FP), R3
+	MOVW	fd+24(FP), R4
+	MOVW	off+28(FP), R5
+
+	MOVD	$SYS_mmap, R8
+	SVC
+	MOVD	R0, ret+32(FP)
+	RET
+
+TEXT runtime·munmap(SB),NOSPLIT,$-8
+	MOVD	addr+0(FP), R0
+	MOVD	n+8(FP), R1
+	MOVD	$SYS_munmap, R8
+	SVC
+	CMN	$4095, R0
+	BCC	cool
+	MOVD	R0, 0xf0(R0)
+cool:
+	RET
+
+TEXT runtime·madvise(SB),NOSPLIT,$-8
+	MOVD	addr+0(FP), R0
+	MOVD	n+8(FP), R1
+	MOVW	flags+16(FP), R2
+	MOVD	$SYS_madvise, R8
+	SVC
+	// ignore failure - maybe pages are locked
+	RET
+
+// int64 futex(int32 *uaddr, int32 op, int32 val,
+//	struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex(SB),NOSPLIT,$-8
+	MOVD	addr+0(FP), R0
+	MOVW	op+8(FP), R1
+	MOVW	val+12(FP), R2
+	MOVD	ts+16(FP), R3
+	MOVD	addr2+24(FP), R4
+	MOVW	val3+32(FP), R5
+	MOVD	$SYS_futex, R8
+	SVC
+	MOVW	R0, ret+40(FP)
+	RET
+
+// int64 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
+TEXT runtime·clone(SB),NOSPLIT,$-8
+	MOVW	flags+0(FP), R0
+	MOVD	stk+8(FP), R1
+
+	// Copy mp, gp, fn off parent stack for use by child.
+	MOVD	mm+16(FP), R10
+	MOVD	gg+24(FP), R11
+	MOVD	fn+32(FP), R12
+
+	MOVD	R10, -8(R1)
+	MOVD	R11, -16(R1)
+	MOVD	R12, -24(R1)
+	MOVD	$1234, R10
+	MOVD	R10, -32(R1)
+
+	MOVD	$SYS_clone, R8
+	SVC
+
+	// In parent, return.
+	CMP	ZR, R0
+	BEQ	child
+	MOVW	R0, ret+40(FP)
+	RET
+child:
+
+	// In child, on new stack.
+	MOVD	-32(RSP), R10
+	MOVD	$1234, R0
+	CMP	R0, R10
+	BEQ	good
+	MOVD	$0, R0
+	MOVD	R0, (R0)	// crash
+
+	// Initialize m->procid to Linux tid
+good:
+	MOVD	$SYS_gettid, R8
+	SVC
+
+	MOVD	-24(RSP), R12
+	MOVD	-16(RSP), R11
+	MOVD	-8(RSP), R10
+
+	MOVD	R0, m_procid(R10)
+
+	// TODO: setup TLS.
+
+	// In child, set up new stack
+	MOVD	R10, g_m(R11)
+	MOVD	R11, g
+	//CALL	runtime·stackcheck(SB)
+
+	// Call fn
+	MOVD	R12, R0
+	BL	(R0)
+
+	// It shouldn't return.	 If it does, exit
+	MOVW	$111, R0
+again:
+	MOVD	$SYS_exit_group, R8
+	SVC
+	B	again	// keep exiting
+
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
+	MOVD	new+0(FP), R0
+	MOVD	old+8(FP), R1
+	MOVD	$SYS_sigaltstack, R8
+	SVC
+	CMN	$4095, R0
+	BCC	ok
+	MOVD	$0, R0
+	MOVD	R0, (R0)	// crash
+ok:
+	RET
+
+TEXT runtime·osyield(SB),NOSPLIT,$-8
+	MOVD	$SYS_sched_yield, R8
+	SVC
+	RET
+
+TEXT runtime·sched_getaffinity(SB),NOSPLIT,$-8
+	MOVD	pid+0(FP), R0
+	MOVD	len+8(FP), R1
+	MOVD	buf+16(FP), R2
+	MOVD	$SYS_sched_getaffinity, R8
+	SVC
+	MOVW	R0, ret+24(FP)
+	RET
+
+// int32 runtime·epollcreate(int32 size);
+TEXT runtime·epollcreate(SB),NOSPLIT,$-8
+	MOVW	$0, R0
+	MOVD	$SYS_epoll_create1, R8
+	SVC
+	MOVW	R0, ret+8(FP)
+	RET
+
+// int32 runtime·epollcreate1(int32 flags);
+TEXT runtime·epollcreate1(SB),NOSPLIT,$-8
+	MOVW	flags+0(FP), R0
+	MOVD	$SYS_epoll_create1, R8
+	SVC
+	MOVW	R0, ret+8(FP)
+	RET
+
+// func epollctl(epfd, op, fd int32, ev *epollEvent) int
+TEXT runtime·epollctl(SB),NOSPLIT,$-8
+	MOVW	epfd+0(FP), R0
+	MOVW	op+4(FP), R1
+	MOVW	fd+8(FP), R2
+	MOVD	ev+16(FP), R3
+	MOVD	$SYS_epoll_ctl, R8
+	SVC
+	MOVW	R0, ret+24(FP)
+	RET
+
+// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
+TEXT runtime·epollwait(SB),NOSPLIT,$-8
+	MOVW	epfd+0(FP), R0
+	MOVD	ev+8(FP), R1
+	MOVW	nev+16(FP), R2
+	MOVW	timeout+20(FP), R3
+	MOVD	$0, R4
+	MOVD	$SYS_epoll_pwait, R8
+	SVC
+	MOVW	R0, ret+24(FP)
+	RET
+
+// void runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$-8
+	MOVW	fd+0(FP), R0  // fd
+	MOVD	$2, R1	// F_SETFD
+	MOVD	$1, R2	// FD_CLOEXEC
+	MOVD	$SYS_fcntl, R8
+	SVC
+	RET
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index c7e3b0b..9db5faf 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -357,6 +357,10 @@
 		if usesLR && waspanic {
 			x := *(*uintptr)(unsafe.Pointer(frame.sp))
 			frame.sp += ptrSize
+			if GOARCH == "arm64" {
+				// arm64 needs 16-byte aligned SP, always
+				frame.sp += ptrSize
+			}
 			f = findfunc(frame.pc)
 			frame.fn = f
 			if f == nil {
diff --git a/src/runtime/unaligned1.go b/src/runtime/unaligned1.go
index 0a88ff2..d3d6c70 100644
--- a/src/runtime/unaligned1.go
+++ b/src/runtime/unaligned1.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build 386 amd64 amd64p32
+// +build 386 amd64 amd64p32 arm64
 
 package runtime
 
diff --git a/src/runtime/zgoarch_386.go b/src/runtime/zgoarch_386.go
index 8aa3da9..79053f1 100644
--- a/src/runtime/zgoarch_386.go
+++ b/src/runtime/zgoarch_386.go
@@ -8,5 +8,6 @@
 const goarch_amd64 = 0
 const goarch_amd64p32 = 0
 const goarch_arm = 0
+const goarch_arm64 = 0
 const goarch_ppc64 = 0
 const goarch_ppc64le = 0
diff --git a/src/runtime/zgoarch_amd64.go b/src/runtime/zgoarch_amd64.go
index eb4f31d..70095f5 100644
--- a/src/runtime/zgoarch_amd64.go
+++ b/src/runtime/zgoarch_amd64.go
@@ -8,5 +8,6 @@
 const goarch_amd64 = 1
 const goarch_amd64p32 = 0
 const goarch_arm = 0
+const goarch_arm64 = 0
 const goarch_ppc64 = 0
 const goarch_ppc64le = 0
diff --git a/src/runtime/zgoarch_amd64p32.go b/src/runtime/zgoarch_amd64p32.go
index c2579e2..9ac3f0b 100644
--- a/src/runtime/zgoarch_amd64p32.go
+++ b/src/runtime/zgoarch_amd64p32.go
@@ -8,5 +8,6 @@
 const goarch_amd64 = 0
 const goarch_amd64p32 = 1
 const goarch_arm = 0
+const goarch_arm64 = 0
 const goarch_ppc64 = 0
 const goarch_ppc64le = 0
diff --git a/src/runtime/zgoarch_arm.go b/src/runtime/zgoarch_arm.go
index 3098bed..c865dc0 100644
--- a/src/runtime/zgoarch_arm.go
+++ b/src/runtime/zgoarch_arm.go
@@ -8,5 +8,6 @@
 const goarch_amd64 = 0
 const goarch_amd64p32 = 0
 const goarch_arm = 1
+const goarch_arm64 = 0
 const goarch_ppc64 = 0
 const goarch_ppc64le = 0
diff --git a/src/runtime/zgoarch_arm64.go b/src/runtime/zgoarch_arm64.go
new file mode 100644
index 0000000..cde5e9f
--- /dev/null
+++ b/src/runtime/zgoarch_arm64.go
@@ -0,0 +1,13 @@
+// generated by gengoos.go using 'go generate'
+
+package runtime
+
+const theGoarch = `arm64`
+
+const goarch_386 = 0
+const goarch_amd64 = 0
+const goarch_amd64p32 = 0
+const goarch_arm = 0
+const goarch_arm64 = 1
+const goarch_ppc64 = 0
+const goarch_ppc64le = 0
diff --git a/src/runtime/zgoarch_ppc64.go b/src/runtime/zgoarch_ppc64.go
index 3097322..13d87d9 100644
--- a/src/runtime/zgoarch_ppc64.go
+++ b/src/runtime/zgoarch_ppc64.go
@@ -8,5 +8,6 @@
 const goarch_amd64 = 0
 const goarch_amd64p32 = 0
 const goarch_arm = 0
+const goarch_arm64 = 0
 const goarch_ppc64 = 1
 const goarch_ppc64le = 0
diff --git a/src/runtime/zgoarch_ppc64le.go b/src/runtime/zgoarch_ppc64le.go
index f4102ac..5d088aa 100644
--- a/src/runtime/zgoarch_ppc64le.go
+++ b/src/runtime/zgoarch_ppc64le.go
@@ -8,5 +8,6 @@
 const goarch_amd64 = 0
 const goarch_amd64p32 = 0
 const goarch_arm = 0
+const goarch_arm64 = 0
 const goarch_ppc64 = 0
 const goarch_ppc64le = 1