github.com/bytedance/sonic@v1.11.7-0.20240517092252-d2edb31b167b/internal/decoder/generic_regabi_amd64.go (about) 1 // +build go1.17,!go1.23 2 3 /* 4 * Copyright 2021 ByteDance Inc. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package decoder 20 21 import ( 22 `encoding/json` 23 `fmt` 24 `reflect` 25 26 `github.com/bytedance/sonic/internal/jit` 27 `github.com/bytedance/sonic/internal/native` 28 `github.com/bytedance/sonic/internal/native/types` 29 `github.com/twitchyliquid64/golang-asm/obj` 30 ) 31 32 /** Crucial Registers: 33 * 34 * ST(R13) && 0(SP) : ro, decoder stack 35 * DF(AX) : ro, decoder flags 36 * EP(BX) : wo, error pointer 37 * IP(R10) : ro, input pointer 38 * IL(R12) : ro, input length 39 * IC(R11) : rw, input cursor 40 * VP(R15) : ro, value pointer (to an interface{}) 41 */ 42 43 const ( 44 _VD_args = 8 // 8 bytes for passing arguments to this functions 45 _VD_fargs = 64 // 64 bytes for passing arguments to other Go functions 46 _VD_saves = 48 // 48 bytes for saving the registers before CALL instructions 47 _VD_locals = 96 // 96 bytes for local variables 48 ) 49 50 const ( 51 _VD_offs = _VD_fargs + _VD_saves + _VD_locals 52 _VD_size = _VD_offs + 8 // 8 bytes for the parent frame pointer 53 ) 54 55 var ( 56 _VAR_ss = _VAR_ss_Vt 57 _VAR_df = jit.Ptr(_SP, _VD_fargs + _VD_saves) 58 ) 59 60 var ( 61 _VAR_ss_Vt = jit.Ptr(_SP, _VD_fargs + _VD_saves + 8) 62 _VAR_ss_Dv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 16) 63 _VAR_ss_Iv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 24) 64 _VAR_ss_Ep = jit.Ptr(_SP, _VD_fargs + _VD_saves + 32) 65 _VAR_ss_Db = jit.Ptr(_SP, _VD_fargs + _VD_saves + 40) 66 _VAR_ss_Dc = jit.Ptr(_SP, _VD_fargs + _VD_saves + 48) 67 ) 68 69 var ( 70 _VAR_R9 = jit.Ptr(_SP, _VD_fargs + _VD_saves + 56) 71 ) 72 type _ValueDecoder struct { 73 jit.BaseAssembler 74 } 75 76 var ( 77 _VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64) 78 _VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72) 79 _VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80) 80 _VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 88) 81 ) 82 83 func (self *_ValueDecoder) build() uintptr { 84 self.Init(self.compile) 85 return *(*uintptr)(self.Load("decode_value", _VD_size, _VD_args, argPtrs_generic, localPtrs_generic)) 86 } 87 88 /** Function Calling Helpers **/ 89 90 func (self *_ValueDecoder) save(r ...obj.Addr) { 91 for i, v := range r { 92 if i > _VD_saves / 8 - 1 { 93 panic("too many registers to save") 94 } else { 95 self.Emit("MOVQ", v, jit.Ptr(_SP, _VD_fargs + int64(i) * 8)) 96 } 97 } 98 } 99 100 func (self *_ValueDecoder) load(r ...obj.Addr) { 101 for i, v := range r { 102 if i > _VD_saves / 8 - 1 { 103 panic("too many registers to load") 104 } else { 105 self.Emit("MOVQ", jit.Ptr(_SP, _VD_fargs + int64(i) * 8), v) 106 } 107 } 108 } 109 110 func (self *_ValueDecoder) call(fn obj.Addr) { 111 self.Emit("MOVQ", fn, _R9) // MOVQ ${fn}, AX 112 self.Rjmp("CALL", _R9) // CALL AX 113 } 114 115 func (self *_ValueDecoder) call_go(fn obj.Addr) { 116 self.save(_REG_go...) // SAVE $REG_go 117 self.call(fn) // CALL ${fn} 118 self.load(_REG_go...) // LOAD $REG_go 119 } 120 121 func (self *_ValueDecoder) callc(fn obj.Addr) { 122 self.save(_IP) 123 self.call(fn) 124 self.load(_IP) 125 } 126 127 func (self *_ValueDecoder) call_c(fn obj.Addr) { 128 self.Emit("XCHGQ", _IC, _BX) 129 self.callc(fn) 130 self.Emit("XCHGQ", _IC, _BX) 131 } 132 133 /** Decoder Assembler **/ 134 135 const ( 136 _S_val = iota + 1 137 _S_arr 138 _S_arr_0 139 _S_obj 140 _S_obj_0 141 _S_obj_delim 142 _S_obj_sep 143 ) 144 145 const ( 146 _S_omask_key = (1 << _S_obj_0) | (1 << _S_obj_sep) 147 _S_omask_end = (1 << _S_obj_0) | (1 << _S_obj) 148 _S_vmask = (1 << _S_val) | (1 << _S_arr_0) 149 ) 150 151 const ( 152 _A_init_len = 1 153 _A_init_cap = 16 154 ) 155 156 const ( 157 _ST_Sp = 0 158 _ST_Vt = _PtrBytes 159 _ST_Vp = _PtrBytes * (types.MAX_RECURSE + 1) 160 ) 161 162 var ( 163 _V_true = jit.Imm(int64(pbool(true))) 164 _V_false = jit.Imm(int64(pbool(false))) 165 _F_value = jit.Imm(int64(native.S_value)) 166 ) 167 168 var ( 169 _V_max = jit.Imm(int64(types.V_MAX)) 170 _E_eof = jit.Imm(int64(types.ERR_EOF)) 171 _E_invalid = jit.Imm(int64(types.ERR_INVALID_CHAR)) 172 _E_recurse = jit.Imm(int64(types.ERR_RECURSE_EXCEED_MAX)) 173 ) 174 175 var ( 176 _F_convTslice = jit.Func(convTslice) 177 _F_convTstring = jit.Func(convTstring) 178 _F_invalid_vtype = jit.Func(invalid_vtype) 179 ) 180 181 var ( 182 _T_map = jit.Type(reflect.TypeOf((map[string]interface{})(nil))) 183 _T_bool = jit.Type(reflect.TypeOf(false)) 184 _T_int64 = jit.Type(reflect.TypeOf(int64(0))) 185 _T_eface = jit.Type(reflect.TypeOf((*interface{})(nil)).Elem()) 186 _T_slice = jit.Type(reflect.TypeOf(([]interface{})(nil))) 187 _T_string = jit.Type(reflect.TypeOf("")) 188 _T_number = jit.Type(reflect.TypeOf(json.Number(""))) 189 _T_float64 = jit.Type(reflect.TypeOf(float64(0))) 190 ) 191 192 var _R_tab = map[int]string { 193 '[': "_decode_V_ARRAY", 194 '{': "_decode_V_OBJECT", 195 ':': "_decode_V_KEY_SEP", 196 ',': "_decode_V_ELEM_SEP", 197 ']': "_decode_V_ARRAY_END", 198 '}': "_decode_V_OBJECT_END", 199 } 200 201 func (self *_ValueDecoder) compile() { 202 self.Emit("SUBQ", jit.Imm(_VD_size), _SP) // SUBQ $_VD_size, SP 203 self.Emit("MOVQ", _BP, jit.Ptr(_SP, _VD_offs)) // MOVQ BP, _VD_offs(SP) 204 self.Emit("LEAQ", jit.Ptr(_SP, _VD_offs), _BP) // LEAQ _VD_offs(SP), BP 205 206 /* initialize the state machine */ 207 self.Emit("XORL", _CX, _CX) // XORL CX, CX 208 self.Emit("MOVQ", _DF, _VAR_df) // MOVQ DF, df 209 /* initialize digital buffer first */ 210 self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_ss_Dc) // MOVQ $_MaxDigitNums, ss.Dcap 211 self.Emit("LEAQ", jit.Ptr(_ST, _DbufOffset), _AX) // LEAQ _DbufOffset(ST), AX 212 self.Emit("MOVQ", _AX, _VAR_ss_Db) // MOVQ AX, ss.Dbuf 213 /* add ST offset */ 214 self.Emit("ADDQ", jit.Imm(_FsmOffset), _ST) // ADDQ _FsmOffset, _ST 215 self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp 216 self.WriteRecNotAX(0, _VP, jit.Ptr(_ST, _ST_Vp), false) // MOVQ VP, ST.Vp[0] 217 self.Emit("MOVQ", jit.Imm(_S_val), jit.Ptr(_ST, _ST_Vt)) // MOVQ _S_val, ST.Vt[0] 218 self.Sjmp("JMP" , "_next") // JMP _next 219 220 /* set the value from previous round */ 221 self.Link("_set_value") // _set_value: 222 self.Emit("MOVL" , jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX 223 self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 224 self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX 225 self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX 226 self.Sjmp("JNC" , "_vtype_error") // JNC _vtype_error 227 self.Emit("XORL" , _SI, _SI) // XORL SI, SI 228 self.Emit("SUBQ" , jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp 229 self.Emit("XCHGQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // XCHGQ ST.Vp[CX], SI 230 self.Emit("MOVQ" , _R8, jit.Ptr(_SI, 0)) // MOVQ R8, (SI) 231 self.WriteRecNotAX(1, _R9, jit.Ptr(_SI, 8), false) // MOVQ R9, 8(SI) 232 233 /* check for value stack */ 234 self.Link("_next") // _next: 235 self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _AX) // MOVQ ST.Sp, AX 236 self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX 237 self.Sjmp("JS" , "_return") // JS _return 238 239 /* fast path: test up to 4 characters manually */ 240 self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL 241 self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF 242 self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX 243 self.Emit("MOVQ" , jit.Imm(_BM_space), _DX) // MOVQ _BM_space, DX 244 self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' ' 245 self.Sjmp("JA" , "_decode_fast") // JA _decode_fast 246 self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX 247 self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast 248 self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC 249 250 /* at least 1 to 3 spaces */ 251 for i := 0; i < 3; i++ { 252 self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL 253 self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF 254 self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX 255 self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' ' 256 self.Sjmp("JA" , "_decode_fast") // JA _decode_fast 257 self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX 258 self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast 259 self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC 260 } 261 262 /* at least 4 spaces */ 263 self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL 264 self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF 265 self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX 266 267 /* fast path: use lookup table to select decoder */ 268 self.Link("_decode_fast") // _decode_fast: 269 self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI 270 self.Sref("_decode_tab", 4) // .... &_decode_tab 271 self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, 0), _AX) // MOVLQSX (DI)(AX*4), AX 272 self.Emit("TESTQ" , _AX, _AX) // TESTQ AX, AX 273 self.Sjmp("JZ" , "_decode_native") // JZ _decode_native 274 self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC 275 self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX 276 self.Rjmp("JMP" , _AX) // JMP AX 277 278 /* decode with native decoder */ 279 self.Link("_decode_native") // _decode_native: 280 self.Emit("MOVQ", _IP, _DI) // MOVQ IP, DI 281 self.Emit("MOVQ", _IL, _SI) // MOVQ IL, SI 282 self.Emit("MOVQ", _IC, _DX) // MOVQ IC, DX 283 self.Emit("LEAQ", _VAR_ss, _CX) // LEAQ ss, CX 284 self.Emit("MOVQ", _VAR_df, _R8) // MOVQ $df, R8 285 self.Emit("BTSQ", jit.Imm(_F_allow_control), _R8) // ANDQ $1<<_F_allow_control, R8 286 self.callc(_F_value) // CALL value 287 self.Emit("MOVQ", _AX, _IC) // MOVQ AX, IC 288 289 /* check for errors */ 290 self.Emit("MOVQ" , _VAR_ss_Vt, _AX) // MOVQ ss.Vt, AX 291 self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX 292 self.Sjmp("JS" , "_parsing_error") 293 self.Sjmp("JZ" , "_invalid_vtype") // JZ _invalid_vtype 294 self.Emit("CMPQ" , _AX, _V_max) // CMPQ AX, _V_max 295 self.Sjmp("JA" , "_invalid_vtype") // JA _invalid_vtype 296 297 /* jump table selector */ 298 self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI 299 self.Sref("_switch_table", 4) // .... &_switch_table 300 self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, -4), _AX) // MOVLQSX -4(DI)(AX*4), AX 301 self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX 302 self.Rjmp("JMP" , _AX) // JMP AX 303 304 /** V_EOF **/ 305 self.Link("_decode_V_EOF") // _decode_V_EOF: 306 self.Emit("MOVL", _E_eof, _EP) // MOVL _E_eof, EP 307 self.Sjmp("JMP" , "_error") // JMP _error 308 309 /** V_NULL **/ 310 self.Link("_decode_V_NULL") // _decode_V_NULL: 311 self.Emit("XORL", _R8, _R8) // XORL R8, R8 312 self.Emit("XORL", _R9, _R9) // XORL R9, R9 313 self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI 314 self.Sjmp("JMP" , "_set_value") // JMP _set_value 315 316 /** V_TRUE **/ 317 self.Link("_decode_V_TRUE") // _decode_V_TRUE: 318 self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8 319 // TODO: maybe modified by users? 320 self.Emit("MOVQ", _V_true, _R9) // MOVQ _V_true, R9 321 self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI 322 self.Sjmp("JMP" , "_set_value") // JMP _set_value 323 324 /** V_FALSE **/ 325 self.Link("_decode_V_FALSE") // _decode_V_FALSE: 326 self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8 327 self.Emit("MOVQ", _V_false, _R9) // MOVQ _V_false, R9 328 self.Emit("LEAQ", jit.Ptr(_IC, -5), _DI) // LEAQ -5(IC), DI 329 self.Sjmp("JMP" , "_set_value") // JMP _set_value 330 331 /** V_ARRAY **/ 332 self.Link("_decode_V_ARRAY") // _decode_V_ARRAY 333 self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX 334 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 335 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX 336 self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX 337 self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char 338 339 /* create a new array */ 340 self.Emit("MOVQ", _T_eface, _AX) // MOVQ _T_eface, AX 341 self.Emit("MOVQ", jit.Imm(_A_init_len), _BX) // MOVQ _A_init_len, BX 342 self.Emit("MOVQ", jit.Imm(_A_init_cap), _CX) // MOVQ _A_init_cap, CX 343 self.call_go(_F_makeslice) // CALL_GO runtime.makeslice 344 345 /* pack into an interface */ 346 self.Emit("MOVQ", jit.Imm(_A_init_len), _BX) // MOVQ _A_init_len, BX 347 self.Emit("MOVQ", jit.Imm(_A_init_cap), _CX) // MOVQ _A_init_cap, CX 348 self.call_go(_F_convTslice) // CALL_GO runtime.convTslice 349 self.Emit("MOVQ", _AX, _R8) // MOVQ AX, R8 350 351 /* replace current state with an array */ 352 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 353 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI 354 self.Emit("MOVQ", jit.Imm(_S_arr), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr, ST.Vt[CX] 355 self.Emit("MOVQ", _T_slice, _AX) // MOVQ _T_slice, AX 356 self.Emit("MOVQ", _AX, jit.Ptr(_SI, 0)) // MOVQ AX, (SI) 357 self.WriteRecNotAX(2, _R8, jit.Ptr(_SI, 8), false) // MOVQ R8, 8(SI) 358 359 /* add a new slot for the first element */ 360 self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX 361 self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE} 362 self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow 363 self.Emit("MOVQ", jit.Ptr(_R8, 0), _AX) // MOVQ (R8), AX 364 self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp 365 self.WritePtrAX(3, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX] 366 self.Emit("MOVQ", jit.Imm(_S_arr_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr_0, ST.Vt[CX] 367 self.Sjmp("JMP" , "_next") // JMP _next 368 369 /** V_OBJECT **/ 370 self.Link("_decode_V_OBJECT") // _decode_V_OBJECT: 371 self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX 372 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 373 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX 374 self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX 375 self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char 376 self.call_go(_F_makemap_small) // CALL_GO runtime.makemap_small 377 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 378 self.Emit("MOVQ", jit.Imm(_S_obj_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj_0, ST.Vt[CX] 379 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI 380 self.Emit("MOVQ", _T_map, _DX) // MOVQ _T_map, DX 381 self.Emit("MOVQ", _DX, jit.Ptr(_SI, 0)) // MOVQ DX, (SI) 382 self.WritePtrAX(4, jit.Ptr(_SI, 8), false) // MOVQ AX, 8(SI) 383 self.Sjmp("JMP" , "_next") // JMP _next 384 385 /** V_STRING **/ 386 self.Link("_decode_V_STRING") // _decode_V_STRING: 387 self.Emit("MOVQ", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX 388 self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX 389 self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX 390 391 /* check for escapes */ 392 self.Emit("CMPQ", _VAR_ss_Ep, jit.Imm(-1)) // CMPQ ss.Ep, $-1 393 self.Sjmp("JNE" , "_unquote") // JNE _unquote 394 self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX 395 self.Emit("LEAQ", jit.Sib(_IP, _CX, 1, 0), _R8) // LEAQ (IP)(CX), R8 396 self.Byte(0x48, 0x8d, 0x3d) // LEAQ (PC), DI 397 self.Sref("_copy_string_end", 4) 398 self.Emit("BTQ", jit.Imm(_F_copy_string), _VAR_df) 399 self.Sjmp("JC", "copy_string") 400 self.Link("_copy_string_end") 401 self.Emit("XORL", _DX, _DX) 402 403 /* strings with no escape sequences */ 404 self.Link("_noescape") // _noescape: 405 self.Emit("MOVL", jit.Imm(_S_omask_key), _DI) // MOVL _S_omask, DI 406 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 407 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _SI) // MOVQ ST.Vt[CX], SI 408 self.Emit("BTQ" , _SI, _DI) // BTQ SI, DI 409 self.Sjmp("JC" , "_object_key") // JC _object_key 410 411 /* check for pre-packed strings, avoid 1 allocation */ 412 self.Emit("TESTQ", _DX, _DX) // TESTQ DX, DX 413 self.Sjmp("JNZ" , "_packed_str") // JNZ _packed_str 414 self.Emit("MOVQ" , _AX, _BX) // MOVQ AX, BX 415 self.Emit("MOVQ" , _R8, _AX) // MOVQ R8, AX 416 self.call_go(_F_convTstring) // CALL_GO runtime.convTstring 417 self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, R9 418 419 /* packed string already in R9 */ 420 self.Link("_packed_str") // _packed_str: 421 self.Emit("MOVQ", _T_string, _R8) // MOVQ _T_string, R8 422 self.Emit("MOVQ", _VAR_ss_Iv, _DI) // MOVQ ss.Iv, DI 423 self.Emit("SUBQ", jit.Imm(1), _DI) // SUBQ $1, DI 424 self.Sjmp("JMP" , "_set_value") // JMP _set_value 425 426 /* the string is an object key, get the map */ 427 self.Link("_object_key") 428 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 429 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI 430 self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI 431 432 /* add a new delimiter */ 433 self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX 434 self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE} 435 self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow 436 self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp 437 self.Emit("MOVQ", jit.Imm(_S_obj_delim), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj_delim, ST.Vt[CX] 438 439 /* add a new slot int the map */ 440 self.Emit("MOVQ", _AX, _DI) // MOVQ AX, DI 441 self.Emit("MOVQ", _T_map, _AX) // MOVQ _T_map, AX 442 self.Emit("MOVQ", _SI, _BX) // MOVQ SI, BX 443 self.Emit("MOVQ", _R8, _CX) // MOVQ R9, CX 444 self.call_go(_F_mapassign_faststr) // CALL_GO runtime.mapassign_faststr 445 446 /* add to the pointer stack */ 447 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 448 self.WritePtrAX(6, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX] 449 self.Sjmp("JMP" , "_next") // JMP _next 450 451 /* allocate memory to store the string header and unquoted result */ 452 self.Link("_unquote") // _unquote: 453 self.Emit("ADDQ", jit.Imm(15), _AX) // ADDQ $15, AX 454 self.Emit("MOVQ", _T_byte, _BX) // MOVQ _T_byte, BX 455 self.Emit("MOVB", jit.Imm(0), _CX) // MOVB $0, CX 456 self.call_go(_F_mallocgc) // CALL_GO runtime.mallocgc 457 self.Emit("MOVQ", _AX, _R9) // MOVQ AX, R9 458 459 /* prepare the unquoting parameters */ 460 self.Emit("MOVQ" , _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX 461 self.Emit("LEAQ" , jit.Sib(_IP, _CX, 1, 0), _DI) // LEAQ (IP)(CX), DI 462 self.Emit("NEGQ" , _CX) // NEGQ CX 463 self.Emit("LEAQ" , jit.Sib(_IC, _CX, 1, -1), _SI) // LEAQ -1(IC)(CX), SI 464 self.Emit("LEAQ" , jit.Ptr(_R9, 16), _DX) // LEAQ 16(R8), DX 465 self.Emit("LEAQ" , _VAR_ss_Ep, _CX) // LEAQ ss.Ep, CX 466 self.Emit("XORL" , _R8, _R8) // XORL R8, R8 467 self.Emit("BTQ" , jit.Imm(_F_disable_urc), _VAR_df) // BTQ ${_F_disable_urc}, fv 468 self.Emit("SETCC", _R8) // SETCC R8 469 self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8 470 471 /* unquote the string, with R9 been preserved */ 472 self.Emit("MOVQ", _R9, _VAR_R9) // SAVE R9 473 self.call_c(_F_unquote) // CALL unquote 474 self.Emit("MOVQ", _VAR_R9, _R9) // LOAD R9 475 476 /* check for errors */ 477 self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX 478 self.Sjmp("JS" , "_unquote_error") // JS _unquote_error 479 self.Emit("MOVL" , jit.Imm(1), _DX) // MOVL $1, DX 480 self.Emit("LEAQ" , jit.Ptr(_R9, 16), _R8) // ADDQ $16, R8 481 self.Emit("MOVQ" , _R8, jit.Ptr(_R9, 0)) // MOVQ R8, (R9) 482 self.Emit("MOVQ" , _AX, jit.Ptr(_R9, 8)) // MOVQ AX, 8(R9) 483 self.Sjmp("JMP" , "_noescape") // JMP _noescape 484 485 /** V_DOUBLE **/ 486 self.Link("_decode_V_DOUBLE") // _decode_V_DOUBLE: 487 self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df 488 self.Sjmp("JC" , "_use_number") // JC _use_number 489 self.Emit("MOVSD", _VAR_ss_Dv, _X0) // MOVSD ss.Dv, X0 490 self.Sjmp("JMP" , "_use_float64") // JMP _use_float64 491 492 /** V_INTEGER **/ 493 self.Link("_decode_V_INTEGER") // _decode_V_INTEGER: 494 self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df 495 self.Sjmp("JC" , "_use_number") // JC _use_number 496 self.Emit("BTQ" , jit.Imm(_F_use_int64), _VAR_df) // BTQ _F_use_int64, df 497 self.Sjmp("JC" , "_use_int64") // JC _use_int64 498 //TODO: use ss.Dv directly 499 self.Emit("MOVSD", _VAR_ss_Dv, _X0) // MOVSD ss.Dv, X0 500 501 /* represent numbers as `float64` */ 502 self.Link("_use_float64") // _use_float64: 503 self.Emit("MOVQ" , _X0, _AX) // MOVQ X0, AX 504 self.call_go(_F_convT64) // CALL_GO runtime.convT64 505 self.Emit("MOVQ" , _T_float64, _R8) // MOVQ _T_float64, R8 506 self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, R9 507 self.Emit("MOVQ" , _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI 508 self.Sjmp("JMP" , "_set_value") // JMP _set_value 509 510 /* represent numbers as `json.Number` */ 511 self.Link("_use_number") // _use_number 512 self.Emit("MOVQ", _VAR_ss_Ep, _AX) // MOVQ ss.Ep, AX 513 self.Emit("LEAQ", jit.Sib(_IP, _AX, 1, 0), _SI) // LEAQ (IP)(AX), SI 514 self.Emit("MOVQ", _IC, _CX) // MOVQ IC, CX 515 self.Emit("SUBQ", _AX, _CX) // SUBQ AX, CX 516 self.Emit("MOVQ", _SI, _AX) // MOVQ SI, AX 517 self.Emit("MOVQ", _CX, _BX) // MOVQ CX, BX 518 self.call_go(_F_convTstring) // CALL_GO runtime.convTstring 519 self.Emit("MOVQ", _T_number, _R8) // MOVQ _T_number, R8 520 self.Emit("MOVQ", _AX, _R9) // MOVQ AX, R9 521 self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI 522 self.Sjmp("JMP" , "_set_value") // JMP _set_value 523 524 /* represent numbers as `int64` */ 525 self.Link("_use_int64") // _use_int64: 526 self.Emit("MOVQ", _VAR_ss_Iv, _AX) // MOVQ ss.Iv, AX 527 self.call_go(_F_convT64) // CALL_GO runtime.convT64 528 self.Emit("MOVQ", _T_int64, _R8) // MOVQ _T_int64, R8 529 self.Emit("MOVQ", _AX, _R9) // MOVQ AX, R9 530 self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI 531 self.Sjmp("JMP" , "_set_value") // JMP _set_value 532 533 /** V_KEY_SEP **/ 534 self.Link("_decode_V_KEY_SEP") // _decode_V_KEY_SEP: 535 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 536 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX 537 self.Emit("CMPQ", _AX, jit.Imm(_S_obj_delim)) // CMPQ AX, _S_obj_delim 538 self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char 539 self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX] 540 self.Emit("MOVQ", jit.Imm(_S_obj), jit.Sib(_ST, _CX, 8, _ST_Vt - 8)) // MOVQ _S_obj, ST.Vt[CX - 1] 541 self.Sjmp("JMP" , "_next") // JMP _next 542 543 /** V_ELEM_SEP **/ 544 self.Link("_decode_V_ELEM_SEP") // _decode_V_ELEM_SEP: 545 self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 546 self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX 547 self.Emit("CMPQ" , _AX, jit.Imm(_S_arr)) 548 self.Sjmp("JE" , "_array_sep") // JZ _next 549 self.Emit("CMPQ" , _AX, jit.Imm(_S_obj)) // CMPQ _AX, _S_arr 550 self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char 551 self.Emit("MOVQ" , jit.Imm(_S_obj_sep), jit.Sib(_ST, _CX, 8, _ST_Vt)) 552 self.Sjmp("JMP" , "_next") // JMP _next 553 554 /* arrays */ 555 self.Link("_array_sep") 556 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI 557 self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI 558 self.Emit("MOVQ", jit.Ptr(_SI, 8), _DX) // MOVQ 8(SI), DX 559 self.Emit("CMPQ", _DX, jit.Ptr(_SI, 16)) // CMPQ DX, 16(SI) 560 self.Sjmp("JAE" , "_array_more") // JAE _array_more 561 562 /* add a slot for the new element */ 563 self.Link("_array_append") // _array_append: 564 self.Emit("ADDQ", jit.Imm(1), jit.Ptr(_SI, 8)) // ADDQ $1, 8(SI) 565 self.Emit("MOVQ", jit.Ptr(_SI, 0), _SI) // MOVQ (SI), SI 566 self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX 567 self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE} 568 self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow 569 self.Emit("SHLQ", jit.Imm(1), _DX) // SHLQ $1, DX 570 self.Emit("LEAQ", jit.Sib(_SI, _DX, 8, 0), _SI) // LEAQ (SI)(DX*8), SI 571 self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp 572 self.WriteRecNotAX(7 , _SI, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ SI, ST.Vp[CX] 573 self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX} 574 self.Sjmp("JMP" , "_next") // JMP _next 575 576 /** V_ARRAY_END **/ 577 self.Link("_decode_V_ARRAY_END") // _decode_V_ARRAY_END: 578 self.Emit("XORL", _DX, _DX) // XORL DX, DX 579 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 580 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX 581 self.Emit("CMPQ", _AX, jit.Imm(_S_arr_0)) // CMPQ AX, _S_arr_0 582 self.Sjmp("JE" , "_first_item") // JE _first_item 583 self.Emit("CMPQ", _AX, jit.Imm(_S_arr)) // CMPQ AX, _S_arr 584 self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char 585 self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp 586 self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX] 587 self.Sjmp("JMP" , "_next") // JMP _next 588 589 /* first element of an array */ 590 self.Link("_first_item") // _first_item: 591 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 592 self.Emit("SUBQ", jit.Imm(2), jit.Ptr(_ST, _ST_Sp)) // SUBQ $2, ST.Sp 593 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp - 8), _SI) // MOVQ ST.Vp[CX - 1], SI 594 self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI 595 self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp - 8)) // MOVQ DX, ST.Vp[CX - 1] 596 self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX] 597 self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI) 598 self.Sjmp("JMP" , "_next") // JMP _next 599 600 /** V_OBJECT_END **/ 601 self.Link("_decode_V_OBJECT_END") // _decode_V_OBJECT_END: 602 self.Emit("MOVL", jit.Imm(_S_omask_end), _DI) // MOVL _S_omask, DI 603 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 604 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX 605 self.Emit("BTQ" , _AX, _DI) 606 self.Sjmp("JNC" , "_invalid_char") // JNE _invalid_char 607 self.Emit("XORL", _AX, _AX) // XORL AX, AX 608 self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp 609 self.Emit("MOVQ", _AX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ AX, ST.Vp[CX] 610 self.Sjmp("JMP" , "_next") // JMP _next 611 612 /* return from decoder */ 613 self.Link("_return") // _return: 614 self.Emit("XORL", _EP, _EP) // XORL EP, EP 615 self.Emit("MOVQ", _EP, jit.Ptr(_ST, _ST_Vp)) // MOVQ EP, ST.Vp[0] 616 self.Link("_epilogue") // _epilogue: 617 self.Emit("SUBQ", jit.Imm(_FsmOffset), _ST) // SUBQ _FsmOffset, _ST 618 self.Emit("MOVQ", jit.Ptr(_SP, _VD_offs), _BP) // MOVQ _VD_offs(SP), BP 619 self.Emit("ADDQ", jit.Imm(_VD_size), _SP) // ADDQ $_VD_size, SP 620 self.Emit("RET") // RET 621 622 /* array expand */ 623 self.Link("_array_more") // _array_more: 624 self.Emit("MOVQ" , _T_eface, _AX) // MOVQ _T_eface, AX 625 self.Emit("MOVQ" , jit.Ptr(_SI, 0), _BX) // MOVQ (SI), BX 626 self.Emit("MOVQ" , jit.Ptr(_SI, 8), _CX) // MOVQ 8(SI), CX 627 self.Emit("MOVQ" , jit.Ptr(_SI, 16), _DI) // MOVQ 16(SI), DI 628 self.Emit("MOVQ" , _DI, _SI) // MOVQ DI, 24(SP) 629 self.Emit("SHLQ" , jit.Imm(1), _SI) // SHLQ $1, SI 630 self.call_go(_F_growslice) // CALL_GO runtime.growslice 631 self.Emit("MOVQ" , _AX, _DI) // MOVQ AX, DI 632 self.Emit("MOVQ" , _BX, _DX) // MOVQ BX, DX 633 self.Emit("MOVQ" , _CX, _AX) // MOVQ CX, AX 634 635 /* update the slice */ 636 self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX 637 self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI 638 self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI 639 self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI) 640 self.Emit("MOVQ", _AX, jit.Ptr(_SI, 16)) // MOVQ AX, 16(AX) 641 self.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI) 642 self.Sjmp("JMP" , "_array_append") // JMP _array_append 643 644 /* copy string */ 645 self.Link("copy_string") // pointer: R8, length: AX, return addr: DI 646 self.Emit("MOVQ", _R8, _VAR_cs_p) 647 self.Emit("MOVQ", _AX, _VAR_cs_n) 648 self.Emit("MOVQ", _DI, _VAR_cs_LR) 649 self.Emit("MOVQ", _AX, _BX) 650 self.Emit("MOVQ", _AX, _CX) 651 self.Emit("MOVQ", _T_byte, _AX) 652 self.call_go(_F_makeslice) 653 self.Emit("MOVQ", _AX, _VAR_cs_d) 654 self.Emit("MOVQ", _VAR_cs_p, _BX) 655 self.Emit("MOVQ", _VAR_cs_n, _CX) 656 self.call_go(_F_memmove) 657 self.Emit("MOVQ", _VAR_cs_d, _R8) 658 self.Emit("MOVQ", _VAR_cs_n, _AX) 659 self.Emit("MOVQ", _VAR_cs_LR, _DI) 660 self.Rjmp("JMP", _DI) 661 662 /* error handlers */ 663 self.Link("_stack_overflow") 664 self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP 665 self.Sjmp("JMP" , "_error") // JMP _error 666 self.Link("_vtype_error") // _vtype_error: 667 self.Emit("MOVQ" , _DI, _IC) // MOVQ DI, IC 668 self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP 669 self.Sjmp("JMP" , "_error") // JMP _error 670 self.Link("_invalid_char") // _invalid_char: 671 self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC 672 self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP 673 self.Sjmp("JMP" , "_error") // JMP _error 674 self.Link("_unquote_error") // _unquote_error: 675 self.Emit("MOVQ" , _VAR_ss_Iv, _IC) // MOVQ ss.Iv, IC 676 self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC 677 self.Link("_parsing_error") // _parsing_error: 678 self.Emit("NEGQ" , _AX) // NEGQ AX 679 self.Emit("MOVQ" , _AX, _EP) // MOVQ AX, EP 680 self.Link("_error") // _error: 681 self.Emit("PXOR" , _X0, _X0) // PXOR X0, X0 682 self.Emit("MOVOU", _X0, jit.Ptr(_VP, 0)) // MOVOU X0, (VP) 683 self.Sjmp("JMP" , "_epilogue") // JMP _epilogue 684 685 /* invalid value type, never returns */ 686 self.Link("_invalid_vtype") 687 self.call_go(_F_invalid_vtype) // CALL invalid_type 688 self.Emit("UD2") // UD2 689 690 /* switch jump table */ 691 self.Link("_switch_table") // _switch_table: 692 self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0 693 self.Sref("_decode_V_NULL", -4) // SREF &_decode_V_NULL, $-4 694 self.Sref("_decode_V_TRUE", -8) // SREF &_decode_V_TRUE, $-8 695 self.Sref("_decode_V_FALSE", -12) // SREF &_decode_V_FALSE, $-12 696 self.Sref("_decode_V_ARRAY", -16) // SREF &_decode_V_ARRAY, $-16 697 self.Sref("_decode_V_OBJECT", -20) // SREF &_decode_V_OBJECT, $-20 698 self.Sref("_decode_V_STRING", -24) // SREF &_decode_V_STRING, $-24 699 self.Sref("_decode_V_DOUBLE", -28) // SREF &_decode_V_DOUBLE, $-28 700 self.Sref("_decode_V_INTEGER", -32) // SREF &_decode_V_INTEGER, $-32 701 self.Sref("_decode_V_KEY_SEP", -36) // SREF &_decode_V_KEY_SEP, $-36 702 self.Sref("_decode_V_ELEM_SEP", -40) // SREF &_decode_V_ELEM_SEP, $-40 703 self.Sref("_decode_V_ARRAY_END", -44) // SREF &_decode_V_ARRAY_END, $-44 704 self.Sref("_decode_V_OBJECT_END", -48) // SREF &_decode_V_OBJECT_END, $-48 705 706 /* fast character lookup table */ 707 self.Link("_decode_tab") // _decode_tab: 708 self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0 709 710 /* generate rest of the tabs */ 711 for i := 1; i < 256; i++ { 712 if to, ok := _R_tab[i]; ok { 713 self.Sref(to, -int64(i) * 4) 714 } else { 715 self.Byte(0x00, 0x00, 0x00, 0x00) 716 } 717 } 718 } 719 720 /** Generic Decoder **/ 721 722 var ( 723 _subr_decode_value = new(_ValueDecoder).build() 724 ) 725 726 //go:nosplit 727 func invalid_vtype(vt types.ValueType) { 728 throw(fmt.Sprintf("invalid value type: %d", vt)) 729 }