+
+;;; Helper to hide the fact that thread access on Windows needs one more
+;;; instruction, needs the FS prefix in that instruction _instead_ of
+;;; the actual load/store, and partially hide the resulting need for a
+;;; temporary TN when the non-windows might have have dereferenced an EA
+;;; without a TN as a base.
+
+(defmacro with-tls-ea ((ea-var &key base
+ base-already-live-p
+ (disp-type :constant)
+ (disp 0))
+ &body body)
+ "Execute BODY with various magic. BODY is expected to emit instructions.
+
+ In the body, EA-VAR will be an alias for an EA which BODY can use to
+ perform a thread-local load or store.
+
+ Within the body, :MAYBE-FS will be replaced with :FS or NIL,
+ depending on the target, and needs to be included in any instruction
+ performing an access through the EA.
+
+ DISP-TYPE must be :INDEX, or :CONSTANT, and DISP must be an EA/TN,
+ or an expression returning an integer, respectively.
+
+ BASE must be a temporary TN, except in the following situation: BASE
+ will be unused when DISP-TYPE is constant, BASE-ALREADY-LIVE-P is
+ true, _and_ we're on POSIX. This is an intentional optimization, and
+ the caller needs to take care to ignore the TN in this case, or can
+ omit this parameter.
+
+ BASE-ALREADY-LIVE-P means that at run-time, the BASE register already
+ holds an offset that we should add to instead of overwriting it.
+ The value of the BASE register is undefined following the macro invocation."
+ (check-type base-already-live-p boolean)
+ (check-type disp-type (member :index :constant))
+ #!-(and win32 sb-thread)
+ (let ((body (subst :fs :maybe-fs body)))
+ (ecase disp-type
+ (:constant
+ `(progn
+ ,@(subst (if base-already-live-p
+ ;; use BASE and DISP
+ `(make-ea :dword :base ,base :disp ,disp)
+ ;; BASE not live and not needed, just use DISP
+ `(make-ea :dword :disp ,disp))
+ ea-var
+ body)))
+ (:index
+ ;; need to use BASE in any case; and DISP is an EA
+ `(progn
+ (inst ,(if base-already-live-p 'add 'mov) ,base ,disp)
+ ,@(subst `(make-ea :dword :base ,base)
+ ea-var
+ body)))))
+ #!+(and win32 sb-thread)
+ ;; goes through a temporary register to add the thread address into it
+ (multiple-value-bind (constant-disp ea-disp)
+ (ecase disp-type
+ (:constant (values disp nil))
+ (:index (values 0 disp)))
+ `(progn
+ ,@(when ea-disp
+ `((inst ,(if base-already-live-p 'add 'mov) ,base ,ea-disp)))
+ (inst ,(if (or base-already-live-p ea-disp) 'add 'mov)
+ ,base
+ (make-ea :dword :disp +win32-tib-arbitrary-field-offset+)
+ :fs)
+ ,@(subst `(make-ea :dword :base ,base :disp ,constant-disp)
+ ea-var
+ (subst nil :maybe-fs body)))))