/* Copyright (C) 2008 The Android Open Source Project
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */

   /*
    * File: OP_FILLED_NEW_ARRAY.S
    *
    * Code: Constructs and fills an array with the given data. Provides
    *
    * For: float-to-int
    *
    * Description: Construct an array of the given type and size,
    *              filling it with the supplied contents. The type
    *              must be an array type. The array's contents
    *              must be single-word. The constructed instance
    *              is stored as a result in the same way that the
    *              method invocation instructions store their results,
    *              so the constructed instance must be moved to a
    *              register with a subsequent move-result-object
    *              instruction.
    *
    * Format: B|A|op CCCC G|F|E|D (35c)
    *         AA|op BBBB CCCC (3rc) (range)
    *
    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, vtaboff@CCCC
    *         [B=4] op {vD, vE, vF, vG}, vtaboff@CCCC
    *         [B=3] op {vD, vE, vF}, vtaboff@CCCC
    *         [B=2] op {vD, vE}, vtaboff@CCCC
    *         [B=1] op {vD}, vtaboff@CCCC
    *
    *         op {vCCCC .. vNNNN}, meth@BBBB
    *         op {vCCCC .. vNNNN}, type@BBBB
    */

%default { "isrange":"0" }

    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
    movl        offGlue_methodClassDex(%edx), %edx # %edx<- glue->methodClassDex
    movl        offDvmDex_pResClasses(%edx), %edx # %edx<- glue->methodClassDex->pResClasses
    FETCH       1, %ecx                 # %ecx<- BBBB
    EXPORT_PC
    movl (%edx, %ecx, 4), %eax # %eax<- possibly resolved class
    cmp         $$0, %eax               # %eax<- check if already resolved
    jne         .L${opcode}_continue
    jmp         .L${opcode}_break
%break

.L${opcode}_break:
    movl        $$0, -8(%esp)           # push parameter false
    movl        %ecx, -12(%esp)         # push parameter BBBB
    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
    movl        offGlue_method(%edx), %edx # %edx<- glue->method
    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
    movl        %edx, -16(%esp)         # push parameter glue->method->clazz
    lea         -16(%esp), %esp
    call        dvmResolveClass         # call: (const ClassObject* referrer, u4 classIdx,
                                        #        bool fromUnverifiedConstant)
                                        # return: ClassObject*
    lea         16(%esp), %esp
    cmp         $$0, %eax               # check for null return
    je          common_exceptionThrown  # handle exception

   /*
    * On entry:
    *  %eax holds array class
    *  rINST holds BA or AA
    */

.L${opcode}_continue:
    movl        offClassObject_descriptor(%eax), %eax # %eax<- arrayClass->descriptor
    movzbl      1(%eax), %eax           # %eax<- descriptor[1]
    cmpb        $$'I', %al             # check if array of ints
    je          1f
    cmpb        $$'L', %al
    je          1f
    cmpb        $$'[', %al
    jne         .L${opcode}_notimpl     # jump to not implemented
1:
    movl        %eax, sReg0             # save type
    movl        rINST, -12(%esp)        # push parameter length
    movl        %eax, -16(%esp)         # push parameter descriptor[1]
    movl        $$ALLOC_DONT_TRACK, -8(%esp) # push parameter to allocate flags
    .if         (!$isrange)
    shrl        $$4, -12(%esp)          # parameter length is B
    .endif
    lea         -16(%esp), %esp
    call        dvmAllocPrimitiveArray  # call: (char type, size_t length, int allocFlags)
                                        # return: ArrayObject*
    lea         16(%esp), %esp
    cmp         $$0, %eax               # check for null return
    je          common_exceptionThrown  # handle exception

    FETCH       2, %edx                 # %edx<- FEDC or CCCC
    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
    movl        %eax, offGlue_retval(%ecx) # retval<- new array
    lea         offArrayObject_contents(%eax), %eax # %eax<- newArray->contents
    subl        $$1, -12(%esp)          # length--; check for negative
    js          2f                      # if length was zero, finish

   /*
    * copy values from registers into the array
    * %eax=array, %edx=CCCC/FEDC, -12(%esp)=length (from AA or B), rINST=AA/BA
    */

    .if         $isrange
    lea         (rFP, %edx, 4), %ecx    # %ecx<- &fpp[CCCC]
1:
    movl        (%ecx), %edx            # %edx<- %ecx++
    lea         4(%ecx), %ecx           # %ecx++
    movl        %edx, (%eax)            # *contents<- vX
    lea         4(%eax), %eax           # %eax++; contents++
    subl        $$1, -12(%esp)          # length--
    jns         1b                      # or continue at 2
    .else
    cmp         $$4, -12(%esp)          # check length
    jne         1f                      # has four args
    and         $$15, rINST             # rINST<- A
    GET_VREG    rINST                   # rINST<- vA
    subl        $$1, -12(%esp)          # count--
    movl        rINST, 16(%eax)         # contents[4]<- vA
1:
    movl        %edx, %ecx              # %ecx<- %edx; ecx for temp
    andl        $$15, %ecx              # %ecx<- G/F/E/D
    GET_VREG    %ecx                    # %ecx<- vG/vF/vE/vD
    shr         $$4, %edx               # %edx<- put next reg in low 4
    subl        $$1, -12(%esp)          # count--
    movl        %ecx, (%eax)            # *contents<- vX
    lea         4(%eax), %eax           # %eax++; contents++
    jns         1b                      # or continue at 2
    .endif
2:
    cmpb        $$'I', sReg0            # check for int array
    je          3f
    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
    movl        offGlue_retval(%ecx), %eax # Object head
    movl        offGlue_cardTable(%ecx), %ecx # card table base
    shrl        $$GC_CARD_SHIFT, %eax   # convert to card num
    movb        %cl,(%ecx, %eax)        # mark card based on object head
3:
    FINISH      3                       # jump to next instruction

   /*
    * Throw an exception to indicate this mode of filled-new-array
    * has not been implemented.
    */

.L${opcode}_notimpl:
    movl        $$.LstrInternalError, -8(%esp)
    movl        $$.LstrFilledNewArrayNotImpl, -4(%esp)
    lea         -8(%esp), %esp
    call        dvmThrowException # call: (const char* exceptionDescriptor,
                                  #        const char* msg)
                                  # return: void
    lea         8(%esp), %esp
    jmp         common_exceptionThrown

.if         (!$isrange)                 # define in one or the other, not both
.LstrFilledNewArrayNotImpl:
.asciz      "filled-new-array only implemented for 'int'"
.LstrInternalError:
.asciz  "Ljava/lang/InternalError;"
.endif