Răsfoiți Sursa

ARM: Finish basic table indexing and add metamethod handlers.

Mike Pall 14 ani în urmă
părinte
comite
314995aebf
1 a modificat fișierele cu 335 adăugiri și 16 ștergeri
  1. 335 16
      src/buildvm_arm.dasc

+ 335 - 16
src/buildvm_arm.dasc

@@ -53,6 +53,11 @@
 |.define SAVE_MULTRES,	[sp, #4]
 |.define ARG5,		[sp]
 |
+|.define TMPDhi,	[sp, #4]
+|.define TMPDlo,	[sp]
+|.define TMPD,		[sp]
+|.define TMPDp,		sp
+|
 |.macro saveregs
 |  push {r4, r5, r6, r7, r8, r9, r10, r11, lr}
 |  sub sp, sp, CFRAME_SPACE
@@ -189,6 +194,15 @@
 |.macro mv_vmstate, reg, st; mvn reg, #LJ_VMST_..st; .endmacro
 |.macro st_vmstate, reg; str reg, [DISPATCH, #DISPATCH_GL(vmstate)]; .endmacro
 |
+|// Move table write barrier back. Overwrites mark and tmp.
+|.macro barrierback, tab, mark, tmp
+|  ldr tmp, [DISPATCH, #DISPATCH_GL(gc.grayagain)]
+|   bic mark, mark, #LJ_GC_BLACK		// black2gray(tab)
+|  str tab, [DISPATCH, #DISPATCH_GL(gc.grayagain)]
+|   strb mark, tab->marked
+|  str tmp, tab->gclist
+|.endmacro
+|
 |//-----------------------------------------------------------------------
 
 #if !LJ_DUALNUM
@@ -417,25 +431,113 @@ static void build_subroutines(BuildCtx *ctx)
   |
   |//-- Table indexing metamethods -----------------------------------------
   |
+  |->vmeta_tgets1:
+  |  add CARG2, BASE, RB
+  |  b >2
+  |
   |->vmeta_tgets:
-  |  NYI
+  |  sub CARG2, DISPATCH, #-DISPATCH_GL(tmptv)
+  |   mvn CARG4, #~LJ_TTAB
+  |  str TAB:RB, [CARG2]
+  |   str CARG4, [CARG2, #4]
+  |2:
+  |   mvn CARG4, #~LJ_TISNUM
+  |  str STR:RC, TMPDlo
+  |   str CARG4, TMPDhi
+  |  mov CARG3, TMPDp
+  |  b >1
   |
-  |->vmeta_tgetb:
-  |  NYI
+  |->vmeta_tgetb:			// RC = index
+  |  decode_RB8 RB, INS
+  |   str RC, TMPDlo
+  |   mvn CARG4, #~LJ_TISNUM
+  |  add CARG2, BASE, RB
+  |   str CARG4, TMPDhi
+  |  mov CARG3, TMPDp
+  |  b >1
   |
   |->vmeta_tgetv:
-  |  NYI
+  |  add CARG2, BASE, RB
+  |   add CARG3, BASE, RC
+  |1:
+  |   str BASE, L->base
+  |  mov CARG1, L
+  |   str PC, SAVE_PC
+  |  bl extern lj_meta_tget		// (lua_State *L, TValue *o, TValue *k)
+  |  // Returns TValue * (finished) or NULL (metamethod).
+  |  cmp CRET1, #0
+  |  beq >3
+  |  ldrd CARG34, [CRET1]
+  |   ins_next1
+  |   ins_next2
+  |  strd CARG34, [BASE, RA]
+  |   ins_next3
+  |
+  |3:  // Call __index metamethod.
+  |  // BASE = base, L->top = new base, stack = cont/func/t/k
+  |   rsb CARG1, BASE, #FRAME_CONT
+  |  ldr BASE, L->top
+  |    mov NARGS8:RC, #16		// 2 args for func(t, k).
+  |    str PC, [BASE, #-12]		// [cont|PC]
+  |   add PC, CARG1, BASE
+  |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Guaranteed to be a function here.
+  |  b ->vm_call_dispatch_f
   |
   |//-----------------------------------------------------------------------
   |
+  |->vmeta_tsets1:
+  |  add CARG2, BASE, RB
+  |  b >2
+  |
   |->vmeta_tsets:
-  |  NYI
+  |  sub CARG2, DISPATCH, #-DISPATCH_GL(tmptv)
+  |   mvn CARG4, #~LJ_TTAB
+  |  str TAB:RB, [CARG2]
+  |   str CARG4, [CARG2, #4]
+  |2:
+  |   mvn CARG4, #~LJ_TISNUM
+  |  str STR:RC, TMPDlo
+  |   str CARG4, TMPDhi
+  |  mov CARG3, TMPDp
+  |  b >1
   |
-  |->vmeta_tsetb:
-  |  NYI
+  |->vmeta_tsetb:			// RC = index
+  |  decode_RB8 RB, INS
+  |   str RC, TMPDlo
+  |   mvn CARG4, #~LJ_TISNUM
+  |  add CARG2, BASE, RB
+  |   str CARG4, TMPDhi
+  |  mov CARG3, TMPDp
+  |  b >1
   |
   |->vmeta_tsetv:
-  |  NYI
+  |  add CARG2, BASE, RB
+  |   add CARG3, BASE, RC
+  |1:
+  |   str BASE, L->base
+  |  mov CARG1, L
+  |   str PC, SAVE_PC
+  |  bl extern lj_meta_tset		// (lua_State *L, TValue *o, TValue *k)
+  |  // Returns TValue * (finished) or NULL (metamethod).
+  |  cmp CRET1, #0
+  |   ldrd CARG34, [BASE, RA]
+  |  beq >3
+  |   ins_next1
+  |  // NOBARRIER: lj_meta_tset ensures the table is not black.
+  |  strd CARG34, [CRET1]
+  |   ins_next2
+  |   ins_next3
+  |
+  |3:  // Call __newindex metamethod.
+  |  // BASE = base, L->top = new base, stack = cont/func/t/k/(v)
+  |   rsb CARG1, BASE, #FRAME_CONT
+  |  ldr BASE, L->top
+  |    mov NARGS8:RC, #24		// 3 args for func(t, k, v).
+  |   strd CARG34, [BASE, #16]		// Copy value to third argument.
+  |    str PC, [BASE, #-12]		// [cont|PC]
+  |   add PC, CARG1, BASE
+  |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Guaranteed to be a function here.
+  |  b ->vm_call_dispatch_f
   |
   |//-- Comparison metamethods ---------------------------------------------
   |
@@ -1462,7 +1564,43 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     break;
 
   case BC_TGETV:
-    |  NYI
+    |  decode_RB8 RB, INS
+    |   decode_RC8 RC, INS
+    |  // RA = dst*8, RB = table*8, RC = key*8
+    |  ldrd TAB:CARG12, [BASE, RB]
+    |   ldrd CARG34, [BASE, RC]
+    |  checktab CARG2, ->vmeta_tgetv  // STALL: load CARG12.
+    |   checktp CARG4, LJ_TISNUM	// Integer key?
+    |  ldreq CARG4, TAB:CARG1->array
+    |    ldreq CARG2, TAB:CARG1->asize
+    |   bne >9
+    |
+    |  add CARG4, CARG4, CARG3, lsl #3
+    |    cmp CARG3, CARG2		// In array part?
+    |  ldrdlo CARG34, [CARG4]
+    |    bhs ->vmeta_tgetv
+    |   ins_next1
+    |  checktp CARG4, LJ_TNIL
+    |  beq >5
+    |1:
+    |   ins_next2
+    |  strd CARG34, [BASE, RA]
+    |   ins_next3
+    |
+    |5:  // Check for __index if table value is nil.
+    |  ldr TAB:CARG2, TAB:CARG1->metatable
+    |  cmp TAB:CARG2, #0
+    |  beq <1				// No metatable: done.
+    |  ldrb CARG2, TAB:CARG2->nomm
+    |  tst CARG2, #1<<MM_index
+    |  bne <1				// 'no __index' flag set: done.
+    |  b ->vmeta_tgetv
+    |
+    |9:
+    |  checktp CARG4, LJ_TSTR		// String key?
+    |   moveq STR:RC, CARG3
+    |  beq ->BC_TGETS_Z
+    |  b ->vmeta_tgetv
     break;
   case BC_TGETS:
     |  decode_RB8 RB, INS
@@ -1471,7 +1609,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  ldrd CARG12, [BASE, RB]
     |   mvn RC, RC
     |   ldr STR:RC, [KBASE, RC, lsl #2]  // STALL: early RC.
-    |  checktab CARG2, ->vmeta_tgets
+    |  checktab CARG2, ->vmeta_tgets1
     |->BC_TGETS_Z:
     |  // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8
     |  ldr CARG3, TAB:CARG1->hmask
@@ -1506,25 +1644,206 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   mov CARG3, #0  // Optional clear of undef. value (during load stall).
     |   mvn CARG4, #~LJ_TNIL
     |  cmp TAB:CARG1, #0
-    |  beq <3					// No metatable: done.
+    |  beq <3				// No metatable: done.
     |  ldrb CARG2, TAB:CARG1->nomm
     |  tst CARG2, #1<<MM_index
-    |  bne <3					// 'no __index' flag set: done.
+    |  bne <3				// 'no __index' flag set: done.
     |  b ->vmeta_tgets
     break;
   case BC_TGETB:
-    |  NYI
+    |  decode_RB8 RB, INS
+    |   and RC, RC, #255
+    |  // RA = dst*8, RB = table*8, RC = index
+    |  ldrd CARG12, [BASE, RB]
+    |  checktab CARG2, ->vmeta_tgetb  // STALL: load CARG12.
+    |   ldr CARG3, TAB:CARG1->asize
+    |  ldr CARG4, TAB:CARG1->array
+    |  lsl CARG2, RC, #3
+    |   cmp RC, CARG3
+    |  ldrdlo CARG34, [CARG4, CARG2]
+    |   bhs ->vmeta_tgetb
+    |   ins_next1  // Overwrites RB!
+    |  checktp CARG4, LJ_TNIL
+    |  beq >5
+    |1:
+    |   ins_next2
+    |  strd CARG34, [BASE, RA]
+    |   ins_next3
+    |
+    |5:  // Check for __index if table value is nil.
+    |  ldr TAB:CARG2, TAB:CARG1->metatable
+    |  cmp TAB:CARG2, #0
+    |  beq <1				// No metatable: done.
+    |  ldrb CARG2, TAB:CARG2->nomm
+    |  tst CARG2, #1<<MM_index
+    |  bne <1				// 'no __index' flag set: done.
+    |  b ->vmeta_tgetb
     break;
 
   case BC_TSETV:
-    |  NYI
+    |  decode_RB8 RB, INS
+    |   decode_RC8 RC, INS
+    |  // RA = src*8, RB = table*8, RC = key*8
+    |  ldrd TAB:CARG12, [BASE, RB]
+    |   ldrd CARG34, [BASE, RC]
+    |  checktab CARG2, ->vmeta_tsetv  // STALL: load CARG12.
+    |   checktp CARG4, LJ_TISNUM	// Integer key?
+    |  ldreq CARG2, TAB:CARG1->array
+    |    ldreq CARG4, TAB:CARG1->asize
+    |   bne >9
+    |
+    |  add CARG2, CARG2, CARG3, lsl #3
+    |    cmp CARG3, CARG4		// In array part?
+    |  ldrlo INS, [CARG2, #4]
+    |    bhs ->vmeta_tsetv
+    |   ins_next1  // Overwrites RB!
+    |  checktp INS, LJ_TNIL
+    |  ldrb INS, TAB:CARG1->marked
+    |   ldrd CARG34, [BASE, RA]
+    |  beq >5
+    |1:
+    |  tst INS, #LJ_GC_BLACK		// isblack(table)
+    |   strd CARG34, [CARG2]
+    |  bne >7
+    |2:
+    |   ins_next2
+    |   ins_next3
+    |
+    |5:  // Check for __newindex if previous value is nil.
+    |  ldr TAB:RA, TAB:CARG1->metatable
+    |  cmp TAB:RA, #0
+    |  beq <1				// No metatable: done.
+    |  ldrb RA, TAB:RA->nomm
+    |  tst RA, #1<<MM_newindex
+    |  bne <1				// 'no __newindex' flag set: done.
+    |  ldr INS, [PC, #-4]		// Restore RA.
+    |  decode_RA8 RA, INS
+    |  b ->vmeta_tsetv
+    |
+    |7:  // Possible table write barrier for the value. Skip valiswhite check.
+    |  barrierback TAB:CARG1, INS, CARG3
+    |  b <2
+    |
+    |9:
+    |  checktp CARG4, LJ_TSTR		// String key?
+    |   moveq STR:RC, CARG3
+    |  beq ->BC_TSETS_Z
+    |  b ->vmeta_tsetv
     break;
   case BC_TSETS:
+    |  decode_RB8 RB, INS
+    |   and RC, RC, #255
+    |  // RA = src*8, RB = table*8, RC = str_const (~)
+    |  ldrd CARG12, [BASE, RB]
+    |   mvn RC, RC
+    |   ldr STR:RC, [KBASE, RC, lsl #2]  // STALL: early RC.
+    |  checktab CARG2, ->vmeta_tsets1
     |->BC_TSETS_Z:
-    |  NYI
+    |  // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8
+    |  ldr CARG3, TAB:CARG1->hmask
+    |   ldr CARG4, STR:RC->hash
+    |    ldr NODE:INS, TAB:CARG1->node
+    |     mov TAB:RB, TAB:CARG1
+    |  and CARG3, CARG3, CARG4			// idx = str->hash & tab->hmask
+    |  add CARG3, CARG3, CARG3, lsl #1
+    |    add NODE:INS, NODE:INS, CARG3, lsl #3	// node = tab->node + idx*3*8
+    |1:
+    |  ldrd CARG12, NODE:INS->key  // STALL: early NODE:INS.
+    |   ldr CARG4, NODE:INS->val.it
+    |    ldr NODE:CARG3, NODE:INS->next
+    |  cmp CARG1, STR:RC
+    |  checktpeq CARG2, LJ_TSTR
+    |  bne >5
+    |  ldrb CARG2, TAB:RB->marked
+    |   checktp CARG4, LJ_TNIL		// Key found, but nil value?
+    |    ldrd CARG34, [BASE, RA]
+    |   beq >4
+    |2:
+    |  tst CARG2, #LJ_GC_BLACK		// isblack(table)
+    |    strd CARG34, NODE:INS->val
+    |  bne >7
+    |3:
+    |   ins_next
+    |
+    |4:  // Check for __newindex if previous value is nil.
+    |  ldr TAB:CARG1, TAB:RB->metatable
+    |  cmp TAB:CARG1, #0
+    |  beq <2				// No metatable: done.
+    |  ldrb CARG1, TAB:CARG1->nomm
+    |  tst CARG1, #1<<MM_newindex
+    |  bne <2				// 'no __newindex' flag set: done.
+    |  b ->vmeta_tsets
+    |
+    |5:  // Follow hash chain.
+    |  movs NODE:INS, NODE:CARG3
+    |  bne <1
+    |  // End of hash chain: key not found, add a new one.
+    |
+    |  // But check for __newindex first.
+    |  ldr TAB:CARG1, TAB:RB->metatable
+    |   mov CARG3, TMPDp
+    |   str PC, SAVE_PC
+    |  cmp TAB:CARG1, #0		// No metatable: continue.
+    |   str BASE, L->base
+    |  ldrbne CARG2, TAB:CARG1->nomm
+    |   mov CARG1, L
+    |  beq >6
+    |  tst CARG2, #1<<MM_newindex
+    |  beq ->vmeta_tsets		// 'no __newindex' flag NOT set: check.
+    |6:
+    |  mvn CARG4, #~LJ_TSTR
+    |   str STR:RC, TMPDlo
+    |   mov CARG2, TAB:RB
+    |  str CARG4, TMPDhi
+    |  bl extern lj_tab_newkey		// (lua_State *L, GCtab *t, TValue *k)
+    |  // Returns TValue *.
+    |  ldr BASE, L->base
+    |  ldrd CARG34, [BASE, RA]
+    |  strd CARG34, [CRET1]
+    |  b <3				// No 2nd write barrier needed.
+    |
+    |7:  // Possible table write barrier for the value. Skip valiswhite check.
+    |  barrierback TAB:CARG1, CARG2, CARG3
+    |  b <3
     break;
   case BC_TSETB:
-    |  NYI
+    |  decode_RB8 RB, INS
+    |   and RC, RC, #255
+    |  // RA = src*8, RB = table*8, RC = index
+    |  ldrd CARG12, [BASE, RB]
+    |  checktab CARG2, ->vmeta_tsetb  // STALL: load CARG12.
+    |   ldr CARG3, TAB:CARG1->asize
+    |  ldr RB, TAB:CARG1->array
+    |  lsl CARG2, RC, #3
+    |   cmp RC, CARG3
+    |  ldrdlo CARG34, [CARG2, RB]!
+    |   bhs ->vmeta_tsetb
+    |   ins_next1  // Overwrites RB!
+    |  checktp CARG4, LJ_TNIL
+    |  ldrb INS, TAB:CARG1->marked
+    |   ldrd CARG34, [BASE, RA]
+    |  beq >5
+    |1:
+    |  tst INS, #LJ_GC_BLACK		// isblack(table)
+    |    strd CARG34, [CARG2]
+    |  bne >7
+    |2:
+    |   ins_next2
+    |   ins_next3
+    |
+    |5:  // Check for __newindex if previous value is nil.
+    |  ldr TAB:RA, TAB:CARG1->metatable
+    |  cmp TAB:RA, #0
+    |  beq <1				// No metatable: done.
+    |  ldrb RA, TAB:RA->nomm
+    |  tst RA, #1<<MM_newindex
+    |  bne <1				// 'no __newindex' flag set: done.
+    |  ldr INS, [PC, #-4]		// Restore INS.
+    |  b ->vmeta_tsetb
+    |
+    |7:  // Possible table write barrier for the value. Skip valiswhite check.
+    |  barrierback TAB:CARG1, INS, CARG3
+    |  b <2
     break;
 
   case BC_TSETM: