| 1493 | } |
| 1494 | |
| 1495 | int asCContext::PushCallState() |
| 1496 | { |
| 1497 | if( m_callStack.GetLength() == m_callStack.GetCapacity() ) |
| 1498 | { |
| 1499 | if (m_engine->ep.maxCallStackSize > 0 && m_callStack.GetLength() >= m_engine->ep.maxCallStackSize*CALLSTACK_FRAME_SIZE) |
| 1500 | { |
| 1501 | // The call stack is too big to grow further |
| 1502 | SetInternalException(TXT_STACK_OVERFLOW); |
| 1503 | return asERROR; |
| 1504 | } |
| 1505 | |
| 1506 | // Allocate space for 10 call states at a time to save time |
| 1507 | m_callStack.AllocateNoConstruct(m_callStack.GetLength() + 10*CALLSTACK_FRAME_SIZE, true); |
| 1508 | } |
| 1509 | m_callStack.SetLengthNoConstruct(m_callStack.GetLength() + CALLSTACK_FRAME_SIZE); |
| 1510 | |
| 1511 | // Separating the loads and stores limits data cache trash, and with a smart compiler |
| 1512 | // could turn into SIMD style loading/storing if available. |
| 1513 | // The compiler can't do this itself due to potential pointer aliasing between the pointers, |
| 1514 | // ie writing to tmp could overwrite the data contained in registers.stackFramePointer for example |
| 1515 | // for all the compiler knows. So introducing the local variable s, which is never referred to by |
| 1516 | // its address we avoid this issue. |
| 1517 | |
| 1518 | asPWORD s[5]; |
| 1519 | s[0] = (asPWORD)m_regs.stackFramePointer; |
| 1520 | s[1] = (asPWORD)m_currentFunction; |
| 1521 | s[2] = (asPWORD)m_regs.programPointer; |
| 1522 | s[3] = (asPWORD)m_regs.stackPointer; |
| 1523 | s[4] = m_stackIndex; |
| 1524 | |
| 1525 | asPWORD *tmp = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE; |
| 1526 | tmp[0] = s[0]; |
| 1527 | tmp[1] = s[1]; |
| 1528 | tmp[2] = s[2]; |
| 1529 | tmp[3] = s[3]; |
| 1530 | tmp[4] = s[4]; |
| 1531 | |
| 1532 | return asSUCCESS; |
| 1533 | } |
| 1534 | |
| 1535 | void asCContext::PopCallState() |
| 1536 | { |
nothing calls this directly
no test coverage detected