/* $NetBSD: asm.h,v 1.44.20.1 2023/07/31 13:36:31 martin Exp $ */ /* * Copyright (c) 1991,1990,1989,1994,1995,1996 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * Assembly coding style * * This file contains macros and register defines to * aid in writing more readable assembly code. * Some rules to make assembly code understandable by * a debugger are also noted. * * The document * * "ALPHA Calling Standard", DEC 27-Apr-90 * * defines (a superset of) the rules and conventions * we use. While we make no promise of adhering to * such standard and its evolution (esp where we * can get faster code paths) it is certainly intended * that we be interoperable with such standard. * * In this sense, this file is a proper part of the * definition of the (software) Alpha architecture. */ /* * Symbolic register names and register saving rules * * Legend: * T Saved by caller (Temporaries) * S Saved by callee (call-Safe registers) */ #define v0 $0 /* (T) return value */ #define t0 $1 /* (T) temporary registers */ #define t1 $2 #define t2 $3 #define t3 $4 #define t4 $5 #define t5 $6 #define t6 $7 #define t7 $8 #define s0 $9 /* (S) call-safe registers */ #define s1 $10 #define s2 $11 #define s3 $12 #define s4 $13 #define s5 $14 #define s6 $15 #define a0 $16 /* (T) argument registers */ #define a1 $17 #define a2 $18 #define a3 $19 #define a4 $20 #define a5 $21 #define t8 $22 /* (T) temporary registers */ #define t9 $23 #define t10 $24 #define t11 $25 #define ra $26 /* (S) return address */ #define t12 $27 /* (T) another temporary */ #define at_reg $28 /* (T) assembler scratch */ #define gp $29 /* (T) (local) data pointer */ #define sp $30 /* (S) stack pointer */ #define zero $31 /* wired zero */ /* Floating point registers (XXXX VERIFY THIS) */ #define fv0 $f0 /* (T) return value (real) */ #define fv1 $f1 /* (T) return value (imaginary)*/ #define ft0 fv1 #define fs0 $f2 /* (S) call-safe registers */ #define fs1 $f3 #define fs2 $f4 #define fs3 $f5 #define fs4 $f6 #define fs5 $f7 #define fs6 $f8 #define fs7 $f9 #define ft1 $f10 /* (T) temporary registers */ #define ft2 $f11 #define ft3 $f12 #define ft4 $f13 #define ft5 $f14 #define ft6 $f15 #define fa0 $f16 /* (T) argument registers */ #define fa1 $f17 #define fa2 $f18 #define fa3 $f19 #define fa4 $f20 #define fa5 $f21 #define ft7 $f22 /* (T) more temporaries */ #define ft8 $f23 #define ft9 $f24 #define ft10 $f25 #define ft11 $f26 #define ft12 $f27 #define ft13 $f28 #define ft14 $f29 #define ft15 $f30 #define fzero $f31 /* wired zero */ /* Other DEC standard names */ #define ai $25 /* (T) argument information */ #define pv $27 /* (T) procedure value */ /* * Useful stuff. */ #ifdef __STDC__ #define __CONCAT(a,b) a ## b #else #define __CONCAT(a,b) a/**/b #endif #define ___CONCAT(a,b) __CONCAT(a,b) /* * Macro to make a local label name. */ #define LLABEL(name,num) ___CONCAT(___CONCAT(L,name),num) /* * * Debuggers need symbol table information to be able to properly * decode a stack trace. The minimum that should be provided is: * * name: * .proc name,numargs * * where "name" is the function's name; * "numargs" how many arguments it expects. For varargs * procedures this should be a negative number, * indicating the minimum required number of * arguments (which is at least 1); * * NESTED functions (functions that call other functions) should define * how they handle their stack frame in a .frame directive: * * .frame framesize, pc_reg, i_mask, f_mask * * where "framesize" is the size of the frame for this function, in bytes. * That is: * new_sp + framesize == old_sp * Framesizes should be rounded to a cacheline size. * Note that old_sp plays the role of a conventional * "frame pointer"; * "pc_reg" is either a register which preserves the caller's PC * or 'std', if std the saved PC should be stored at * old_sp-8 * "i_mask" is a bitmask that indicates which of the integer * registers are saved. See the M_xx defines at the * end for the encoding of this 32bit value. * "f_mask" is the same, for floating point registers. * * Note, 10/31/97: This is interesting but it isn't the way gcc outputs * frame directives and it isn't the way the macros below output them * either. Frame directives look like this: * * .frame $15,framesize,$26,0 * * If no fp is set up then $30 should be used instead of $15. * Also, gdb expects to find a at the beginning * of a procedure. Don't use things like sub sp,framesize,sp for this * reason. End Note 10/31/97. ross@NetBSD.org * * Note that registers should be saved starting at "old_sp-8", where the * return address should be stored. Other registers follow at -16-24-32.. * starting from register 0 (if saved) and up. Then float registers (ifany) * are saved. * * If you need to alias a leaf function, or to provide multiple entry points * use the LEAF() macro for the main entry point and XLEAF() for the other * additional/alternate entry points. * "XLEAF"s must be nested within a "LEAF" and a ".end". * Similar rules for nested routines, e.g. use NESTED/XNESTED * Symbols that should not be exported can be declared with the STATIC_xxx * macros. * * All functions must be terminated by the END macro * * It is conceivable, although currently at the limits of compiler * technology, that while performing inter-procedural optimizations * the compiler/linker be able to avoid unnecessary register spills * if told about the register usage of LEAF procedures (and by transitive * closure of NESTED procedures as well). Assembly code can help * this process using the .reguse directive: * * .reguse i_mask, f_mask * * where the register masks are built as above or-ing M_xx defines. * * * All symbols are internal unless EXPORTed. Symbols that are IMPORTed * must be appropriately described to the debugger. * */ /* * MCOUNT */ #ifndef GPROF #define MCOUNT /* nothing */ #else #define MCOUNT \ .set noat; \ jsr at_reg,_mcount; \ .set at #endif /* * PALVECT, ESETUP, and ERSAVE * Declare a palcode transfer point, and carefully construct * gdb symbols with an unusual _negative_ register-save offset * so that gdb can find the otherwise lost PC and then * invert the vector for traceback. Also, fix up framesize, * allowing for the palframe for the same reason. */ #define PALVECT(_name_) \ ESETUP(_name_); \ ERSAVE() #define ESETUP(_name_) \ /* .loc 1 __LINE__; */ \ .globl _name_; \ .ent _name_ 0; \ _name_:; \ .set noat; \ lda sp,-(FRAME_SW_SIZE*8)(sp); \ .frame $30,(FRAME_SW_SIZE+6)*8,$26,0; /* give gdb the real size */\ .mask 0x4000000,-0x28; \ .set at #define ERSAVE() \ .set noat; \ stq at_reg,(FRAME_AT*8)(sp); \ .set at; \ stq ra,(FRAME_RA*8)(sp); \ /* .loc 1 __LINE__; */ \ bsr ra,exception_save_regs /* jmp/CALL trashes pv/t12 */ /* * LEAF * Declare a global leaf function. * A leaf function does not call other functions AND does not * use any register that is callee-saved AND does not modify * the stack pointer. */ #define LEAF(_name_,_n_args_) \ .globl _name_; \ .ent _name_ 0; \ _name_:; \ .frame sp,0,ra; \ MCOUNT /* should have been .proc _name_,_n_args_; \ .frame 0,ra,0,0 */ #define LEAF_NOPROFILE(_name_,_n_args_) \ .globl _name_; \ .ent _name_ 0; \ _name_:; \ .frame sp,0,ra /* should have been .proc _name_,_n_args_; \ .frame 0,ra,0,0 */ /* * STATIC_LEAF * Declare a local leaf function. */ #define STATIC_LEAF(_name_,_n_args_) \ .ent _name_ 0; \ _name_:; \ .frame sp,0,ra; \ MCOUNT /* should have been .proc _name_,_n_args_; \ .frame 0,ra,0,0 */ /* * XLEAF * Global alias for a leaf function, or alternate entry point */ #define XLEAF(_name_,_n_args_) \ .globl _name_; \ .aent _name_ 0; \ _name_: /* should have been .aproc _name_,_n_args_; */ /* * STATIC_XLEAF * Local alias for a leaf function, or alternate entry point */ #define STATIC_XLEAF(_name_,_n_args_) \ .aent _name_ 0; \ _name_: /* should have been .aproc _name_,_n_args_; */ /* * NESTED * Declare a (global) nested function * A nested function calls other functions and needs * therefore stack space to save/restore registers. */ #define NESTED(_name_, _n_args_, _framesize_, _pc_reg_, _i_mask_, _f_mask_ ) \ .globl _name_; \ .ent _name_ 0; \ _name_:; \ .frame sp,_framesize_,_pc_reg_; \ .livereg _i_mask_,_f_mask_; \ MCOUNT /* should have been .proc _name_,_n_args_; \ .frame _framesize_, _pc_reg_, _i_mask_, _f_mask_ */ #define NESTED_NOPROFILE(_name_, _n_args_, _framesize_, _pc_reg_, _i_mask_, _f_mask_ ) \ .globl _name_; \ .ent _name_ 0; \ _name_:; \ .frame sp,_framesize_,_pc_reg_; \ .livereg _i_mask_,_f_mask_ /* should have been .proc _name_,_n_args_; \ .frame _framesize_, _pc_reg_, _i_mask_, _f_mask_ */ /* * STATIC_NESTED * Declare a local nested function. */ #define STATIC_NESTED(_name_, _n_args_, _framesize_, _pc_reg_, _i_mask_, _f_mask_ ) \ .ent _name_ 0; \ _name_:; \ .frame sp,_framesize_,_pc_reg_; \ .livereg _i_mask_,_f_mask_; \ MCOUNT /* should have been .proc _name_,_n_args_; \ .frame _framesize_, _pc_reg_, _i_mask_, _f_mask_ */ /* * XNESTED * Same as XLEAF, for a nested function. */ #define XNESTED(_name_,_n_args_) \ .globl _name_; \ .aent _name_ 0; \ _name_: /* should have been .aproc _name_,_n_args_; */ /* * STATIC_XNESTED * Same as STATIC_XLEAF, for a nested function. */ #define STATIC_XNESTED(_name_,_n_args_) \ .aent _name_ 0; \ _name_: /* should have been .aproc _name_,_n_args_; */ /* * END * Function delimiter */ #define END(_name_) \ .end _name_ /* * CALL * Function invocation */ #define CALL(_name_) \ /* .loc 1 __LINE__; */ \ jsr ra,_name_; \ ldgp gp,0(ra) /* but this would cover longer jumps br ra,.+4; \ bsr ra,_name_ */ /* * RET * Return from function */ #define RET \ ret zero,(ra),1 /* * EXPORT * Export a symbol */ #define EXPORT(_name_) \ .globl _name_; \ _name_: /* * IMPORT * Make an external name visible, typecheck the size */ #define IMPORT(_name_, _size_) \ .extern _name_,_size_ /* * ABS * Define an absolute symbol */ #define ABS(_name_, _value_) \ .globl _name_; \ _name_ = _value_ /* * BSS * Allocate un-initialized space for a global symbol */ #define BSS(_name_,_numbytes_) \ .comm _name_,_numbytes_ /* * VECTOR * Make an exception entry point look like a called function, * to make it digestible to the debugger (KERNEL only) */ #define VECTOR(_name_, _i_mask_) \ .globl _name_; \ .ent _name_ 0; \ _name_:; \ .mask _i_mask_|IM_EXC,0; \ .frame sp,MSS_SIZE,ra; /* .livereg _i_mask_|IM_EXC,0 */ /* should have been .proc _name_,1; \ .frame MSS_SIZE,$31,_i_mask_,0; \ */ /* * MSG * Allocate space for a message (a read-only ascii string) */ #define ASCIZ .asciz #define MSG(msg,reg,label) \ lda reg, label; \ .data; \ label: ASCIZ msg; \ .text; /* * PRINTF * Print a message */ #define PRINTF(msg,label) \ MSG(msg,a0,label); \ CALL(printf) /* * PANIC * Fatal error (KERNEL) */ #define PANIC(msg,label) \ MSG(msg,a0,label); \ CALL(panic) /* * Register mask defines, used to define both save * and use register sets. * * NOTE: The bit order should HAVE BEEN maintained when saving * registers on the stack: sp goes at the highest * address, gp lower on the stack, etc etc * BUT NOONE CARES ABOUT DEBUGGERS AT MIPS */ #define IM_EXC 0x80000000 #define IM_SP 0x40000000 #define IM_GP 0x20000000 #define IM_AT 0x10000000 #define IM_T12 0x08000000 # define IM_PV IM_T4 #define IM_RA 0x04000000 #define IM_T11 0x02000000 # define IM_AI IM_T3 #define IM_T10 0x01000000 #define IM_T9 0x00800000 #define IM_T8 0x00400000 #define IM_A5 0x00200000 #define IM_A4 0x00100000 #define IM_A3 0x00080000 #define IM_A2 0x00040000 #define IM_A1 0x00020000 #define IM_A0 0x00010000 #define IM_S6 0x00008000 #define IM_S5 0x00004000 #define IM_S4 0x00002000 #define IM_S3 0x00001000 #define IM_S2 0x00000800 #define IM_S1 0x00000400 #define IM_S0 0x00000200 #define IM_T7 0x00000100 #define IM_T6 0x00000080 #define IM_T5 0x00000040 #define IM_T4 0x00000020 #define IM_T3 0x00000010 #define IM_T2 0x00000008 #define IM_T1 0x00000004 #define IM_T0 0x00000002 #define IM_V0 0x00000001 #define FM_T15 0x40000000 #define FM_T14 0x20000000 #define FM_T13 0x10000000 #define FM_T12 0x08000000 #define FM_T11 0x04000000 #define FM_T10 0x02000000 #define FM_T9 0x01000000 #define FM_T8 0x00800000 #define FM_T7 0x00400000 #define FM_A5 0x00200000 #define FM_A4 0x00100000 #define FM_A3 0x00080000 #define FM_A2 0x00040000 #define FM_A1 0x00020000 #define FM_A0 0x00010000 #define FM_T6 0x00008000 #define FM_T5 0x00004000 #define FM_T4 0x00002000 #define FM_T3 0x00001000 #define FM_T2 0x00000800 #define FM_T1 0x00000400 #define FM_S7 0x00000200 #define FM_S6 0x00000100 #define FM_S5 0x00000080 #define FM_S4 0x00000040 #define FM_S3 0x00000020 #define FM_S2 0x00000010 #define FM_S1 0x00000008 #define FM_S0 0x00000004 #define FM_T0 0x00000002 #define FM_V1 FM_T0 #define FM_V0 0x00000001 /* Pull in PAL "function" codes. */ #include /* * System call glue. */ #define SYSCALLNUM(name) \ ___CONCAT(SYS_,name) #define CALLSYS_NOERROR(name) \ ldiq v0, SYSCALLNUM(name); \ call_pal PAL_OSF1_callsys #define LINUX_SYSCALLNUM(name) \ ___CONCAT(LINUX_SYS_,name) #define LINUX_CALLSYS_NOERROR(name) \ ldiq v0, LINUX_SYSCALLNUM(name); \ call_pal PAL_OSF1_callsys /* * Load the global pointer. */ #define LDGP(reg) \ ldgp gp, 0(reg) /* * WEAK_ALIAS: create a weak alias. */ #define WEAK_ALIAS(alias,sym) \ .weak alias; \ alias = sym /* * STRONG_ALIAS: create a strong alias. */ #define STRONG_ALIAS(alias,sym) \ .globl alias; \ alias = sym /* * WARN_REFERENCES: create a warning if the specified symbol is referenced. */ #ifdef __STDC__ #define WARN_REFERENCES(sym,msg) \ .pushsection .gnu.warning. ## sym; \ .ascii msg; \ .popsection #else #define WARN_REFERENCES(sym,msg) \ .pushsection .gnu.warning./**/sym; \ .ascii msg; \ .popsection #endif /* __STDC__ */ /* * Kernel RCS ID tag and copyright macros */ #define __SECTIONSTRING(_sec, _str) \ .pushsection _sec,"MS",@progbits,1; \ .asciz _str; \ .popsection #ifdef _KERNEL #define __KERNEL_RCSID(_n, _s) __SECTIONSTRING(.ident, _s) #define __KERNEL_COPYRIGHT(_n, _s) __SECTIONSTRING(.copyright, _s) #ifdef NO_KERNEL_RCSIDS #undef __KERNEL_RCSID #define __KERNEL_RCSID(_n, _s) /* nothing */ #endif #if defined(MULTIPROCESSOR) /* * Get various per-cpu values. A pointer to our cpu_info structure * is stored in SysValue. These macros clobber v0, t0, t8..t11. * SET_CURLWP also clobbers a0. * * All return values are in v0. */ #define GET_CURLWP \ call_pal PAL_OSF1_rdval /* * Issue barriers to coordinate mutex_exit on this CPU with * mutex_vector_enter on another CPU. * * 1. Any prior mutex_exit by oldlwp must be visible to other * CPUs before we set ci_curlwp := newlwp on this one, * requiring a store-before-store barrier. * * 2. ci_curlwp := newlwp must be visible on all other CPUs * before any subsequent mutex_exit by newlwp can even test * whether there might be waiters, requiring a * store-before-load barrier. * * See kern_mutex.c for details -- this is necessary for * adaptive mutexes to detect whether the lwp is on the CPU in * order to safely block without requiring atomic r/m/w in * mutex_exit. */ #define SET_CURLWP(r) \ ldq v0, L_CPU(r) ; \ mov r, a0 ; \ wmb /* store-before-store XXX patch out if !MP? */ ; \ stq r, CPU_INFO_CURLWP(v0) ; \ mb /* store-before-load XXX patch out if !MP? */ ; \ call_pal PAL_OSF1_wrval #else /* if not MULTIPROCESSOR... */ IMPORT(cpu_info_primary, CPU_INFO_SIZEOF) #define GET_CURLWP lda v0, cpu_info_primary ; \ ldq v0, CPU_INFO_CURLWP(v0) #define SET_CURLWP(r) lda v0, cpu_info_primary ; \ stq r, CPU_INFO_CURLWP(v0) #endif /* MULTIPROCESSOR */ #else #define RCSID(_s) __SECTIONSTRING(.ident, _s) #endif /* _KERNEL */