Skip to content

Potential improvement for virtual typed array access #5517

@Penguinwizzard

Description

@Penguinwizzard

Currently, if we're doing virtual typed array access and don't know whether the offset for the load of an element is necessarily within the 4GB reserved region, we insert bounds checks. However, we seem to do this too often, and in ways that don't give the best perf possible. Consider the following code under -mic:1 -msjrc:1 -bgjit- -oopjit- -dump:globopt:

let arr = new Uint8Array(0x10000000);

function foo(i) {
    i = i | 0;
    if(i<0) i = 0;
    if(i >= 64) i = 63
    return arr[i];
}


foo(0)
foo(1)
foo(2)
for(let i=0;i<100;i++)
{
    foo(i);
}

The typed array load in foo generates the following:

  Line   4: return arr[i];
  Col    5: ^
                       StatementBoundary  #0                                  #0000
    s3[LikelyCanBeTaggedValue_Uint8VirtualArray].var = LdRootFld  s6(s1<s7>[Object]->arr)<0,m,++,s7!,s8,{arr(0)}>[LikelyCanBeTaggedValue_Uint8VirtualArray].var! #0000
    s9(s2).i32      =  FromVar        s2[LikelyCanBeTaggedValue_Int].var!     #0006  Bailout: #0006 (BailOutIntOnly)
                       BailOnNotArray  s3[LikelyCanBeTaggedValue_Uint8VirtualArray].var #0006  Bailout: #0006 (BailOutOnNotArray)
                       BoundCheck     0 <= s9(s2).i32                         #0006  Bailout: #0006 (BailOutOnArrayAccessHelperCall)
                       BoundCheck     s9(s2).i32 < [s3[Uint8VirtualArray].var+32].u32 #0006  Bailout: #0006 (BailOutOnArrayAccessHelperCall)
                       BailTarget                                             #0006  Bailout: #0006 (BailOutShared)
                       Nop                                                    #0006
    s12(s0).i32     =  LdElemI_A      [s3[Uint8VirtualArray][><].var!+s9(s2).i32!].var #0006  Bailout: #0006 (BailOutConventionalTypedArrayAccessOnly)

This shows two potential improvements that we can get:

  1. For virtual typed arrays, the bounds that we have to check are not the array bounds, but the bounds of the 4GB reserved region. This should be easier to prove statically (currently, even if we have an int range for the index that is within this region, we emit the bounds check).

  2. If we can combine the bound check instructions, we can emit better assembly. Currently, these end up lowering as

 GLOBOPT INSTR:                        BoundCheck     0 <= s9(s2).i32                         #0006  Bailout: #0006 (BailOutOnArrayAccessHelperCall)


                       TEST           s9(s2).i32, s9(s2).i32                  #
                       JNSB           $L12                                    #
$L13: [helper]                                                                #
    [s19.u64 < (&BailOutKind)>].u32 = MOV  131072 (0x20000).u32               #
    s21.u64         =  LEA            [s19.u64+XX < (FunctionBody [foo (#1.1), #2])>].u64 #
    [s19.u64+XX < (Unknown)>].u64 = MOV  s21.u64                              #
                       JMP            $L9                                     #
$L12:                                                                         #


 GLOBOPT INSTR:                        BoundCheck     s9(s2).i32 < [s3[Uint8VirtualArray].var+32].u32 #0006  Bailout: #0006 (BailOutOnArrayAccessHelperCall)


                       CMP            s9(s2).i32, [s3[Uint8VirtualArray].var+32].u32 #
                       JLT            $L10                                    #
$L11: [helper]                                                                #
    [s19.u64 < (&BailOutKind)>].u32 = MOV  131072 (0x20000).u32               #
    s20.u64         =  LEA            [s19.u64+XX < (FunctionBody [foo (#1.1), #2])>].u64 #
    [s19.u64+XX < (Unknown)>].u64 = MOV  s20.u64                              #
                       JMP            $L9                                     #
$L10:                                                                         #

We can, if the check is changed to be based on the 4GB region, change this to something along the lines of

SAR unused.u64, index.u64, (32-(elementsize-1))
JNZ helper

which saves one copy of the helper and one branch on the critical path.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions