/*****************************************************************************

  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
  more contributor license agreements.  See the NOTICE file distributed
  with this work for additional information regarding copyright ownership.
  Accellera licenses this file to you 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.

 *****************************************************************************/

/*****************************************************************************

  stack_alignment.cpp -- This example shows the crash of an fxsave instruction 
                         in the sc_thread stack environment, but not in the 
                         original linux process stack, which is correctly 
                         aligned on first function.
                         
  Please note that this test probably runs OK on a faulty implementation in 
  64-bit in general (depending on your libc implementation), but will crash 
  for sure in 32-bit.

  Original Author: Eric Paire, STMicroelectronics

 *****************************************************************************/

/*****************************************************************************

  MODIFICATION LOG - modifiers, enter your name, affiliation, date and
  changes you are making here.

      Name, Affiliation, Date:
  Description of Modification:

 *****************************************************************************/

#include "systemc.h"

/*
 * This program exhibits a bug in the management by QT of the stack of each
 * SystemC process. At least on i686 & x86_64, GCC makes the assumption that
 * the stack is aligned on a 16-byte boundary on each C/C++ function entry.
 * This convention allows GCC to respects constraints of automatic (stack)
 * variable alignment, using the __attribute)__ ((align(X))) GCC extension.
 *
 * The X is known to be 16 for i686 & x86_64, as this is the largest alignment
 * required by instructions operands, actually used by fxsave instruction.
 *
 * The attached code shows up the problem by crashing when fxsave is executed
 * in a SystemC thread, and executing correctly the *same* code on the initial
 * process stack, as initialized by the libc runtime.
 *
 * This misbehavior does not occur systematically for x86_64 (no crash,
 * or crash difficult to reproduce with standard malloc()), but often does
 * with i686. Notice that the instruction with the right alignment is shown
 * when using the myfpxregs address which is aligned on 16-byte boundary.
 */

#if defined(__x86_64__)
#  define FXSAVE "fxsaveq"
#else
#  define FXSAVE "fxsave"
#endif

#if defined(__GNUC__)
#  define ALIGNED_ARRAY( Type, Name, Size, Align ) \
	  Type Name[Size] __attribute__((aligned(Align)))
#elif defined(_MSC_VER)
#  define ALIGNED_ARRAY( Type, Name, Size, Align ) \
      __declspec(align(Align)) Type Name[Size]
#endif

#if defined(__GNUC__) && ( defined(__x86_64__) || defined(__i386__) )
#  define ASM( Assembly ) __asm__ __volatile__( Assembly )
#else
#  define ASM( Assembly ) /* not implemented */
#endif

// Class
SC_MODULE(C)
{ 
public:
    SC_CTOR(C) {
        SC_THREAD(run);
    }
    void run(void) 
    { 
        ALIGNED_ARRAY( char, fpxregs64, 512+15, 16 );

        cout << "Inside C::run() " << endl; 

        // manually enforce alignment (volatile to avoid optmizations)
        char * volatile myfpxregs = fpxregs64;
        while ((uintptr_t)myfpxregs & 0xF)
            myfpxregs = myfpxregs + 1;

        // the "real" requirement: enforced alignment works
        sc_assert( !((uintptr_t)fpxregs64 & 0xF) );
        sc_assert( !((uintptr_t)myfpxregs & 0xF) );
        sc_assert( myfpxregs == fpxregs64 );

        // test assembly on supported platforms
        ASM( FXSAVE " (%0)" :: "r"(myfpxregs) );
        cout << "Between C::run() " << endl; 
        ASM( FXSAVE " %0" : "=m"(fpxregs64) );

        cout << "Out of C::run() " << endl; 
    }
};

int sc_main(int , char** ) {
  C the_C("C");

  ALIGNED_ARRAY( char, fpxregs64, 512, 16 );

  cout << "Inside sc_main() " << endl; 
  ASM( FXSAVE " %0" : "=m"(fpxregs64) );
  sc_start(1, SC_NS);
  cout << "Out of sc_main() " << endl; 
  return 0;
}
