#!/usr/bin/python
#
# Copyright (c) 2012 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#

"""
Responsible for generating actual decoders based on parsed table
representations.

Note: We define a notion of "actual" and "baseline" classes to
distinquish how they are used.

"baseline" instruction decoders are defined for each instruction that
appears in the manual. The "baseline" decoder captures the properties
of the instruction through the corresponding set of defined virtuals
(as defined in "inst_classes.h"). "baseline" instruction decoders are
used to test that individual instructions are decoded as expected.

"actual" instruction decoders are merged "baseline" instruction
decoders, and are used in the production instruction decoder.  The
main purpose of merging instruction decoders into corresponding actual
instruction decoders is to minimize production code size, by
minimizing the number of implementations that must be used by the
production intruction decoder.

To verify that actual and baseline instruction decoders are
equivalent, we test all possible instruction patterns and verify that
the baseline and actual instruction decoders behave the same way
(modulo a concept on whether we cared if condition codes were
assigned).

The main problem with the initial implementation of baseline and
actual instruction decoders is that all are hand written, and the
relationship between the baseline and corresponding actual decoders is
also determined by hand.

To simplify the maintenance of actual instruction decoders, this file
merges baseline classes, based on having the same set of defined
virtual functions (as defined by the corresponding fields in the
decoder tables within file "armv7.table"). The advantage of using this
notion is that changes only need be defined in "armv7.table", and the
generator will figure out how baseline classes are merged (rather than
in the old system where everything was updated by hand).

Verification of the new actual decoders will be the same as before.
We test all possible instruction patterns and verify that the baseline
and actual instruction decoders behave the same way.
"""

import dgen_core
import dgen_decoder
import dgen_output

# Holds the decoder that actuals are defined on.
ACTUAL_DECODER = None

# Holds the map from baseline decoder to the corresponding
# actual decoder.
BASELINE_TO_ACTUAL_MAP = {}

# Holds the map from an actual decoder to the corresponding
# (sorted) set of baseline decoders.
ACTUAL_TO_BASELINE_MAP = {}

# Holds the map from baseline decoder name, to the corresponding
# (sorted) list of baseline decoders with that name.
BASELINE_NAME_TO_BASELINE_MAP = {}

# Holds the map from an actual, to the name we will use for it.
ACTUAL_TO_NAME_MAP = {}

def GetActualDecoders(decoder):
  """Takes the given decoder table, and builds the corresponding
     internal maps, so that we can consistently name actual classes.
     Returns the (sorted) list of actual decoders to build.
     """
  global ACTUAL_DECODER

  # Verify whether actual classes have already been recorded.
  if ACTUAL_DECODER:
    raise Exception("GetActualDecoders: Multiple decoders not allowed.")

  ACTUAL_DECODER = decoder
  actuals = set()

  # Get the list of decoder (actions) defined in the decoder table.
  for baseline in decoder.decoders():
    if not dgen_decoder.ActionDefinesDecoder(baseline): continue

    actual = dgen_decoder.BaselineToActual(baseline)
    actuals.add(actual)

    _AddBaselineToBaselineNameToBaselineMap(baseline)
    _AddToBaselineToActualMap(baseline, actual)
    _AddToActualToBaselineMap(baseline, actual)

  _FixActualToBaselineMap()
  _FixBaselineNameToBaselineMap()
  _DefineActualNames(actuals)
  return sorted(actuals, key=ActualName)

def _DefineActualNames(actuals):
  """Installs a unique name for each actual, based on the baseline decoders
     associated with it.
     """
  global ACTUAL_TO_NAME_MAP

  name_map = {}
  for actual in sorted(actuals):
    bases = ACTUAL_TO_BASELINE_MAP[actual]
    name = 'Unnamed'
    if bases:
      baseline = bases[0]
      name = dgen_decoder.BaselineName(baseline)
    count = name_map.get(name)
    if count == None:
      count = 1
    actual_name = 'Actual_%s_case_%s' % (name, count)
    name_map[name] = count + 1
    ACTUAL_TO_NAME_MAP[actual] = actual_name

def ActualName(actual):
  """Returns the name to use for the actual."""
  return ACTUAL_TO_NAME_MAP[actual]

def AddAutoActualsToDecoder(decoder, tables):
  """Adds the automatically generated class decoders (in files
     "*_actuals.h" and "*_actuals.cc" as the 'actual' class decoder to
     the listed tables, and returns the generated (new) decoder.
     """
  if not tables: return decoder
  GetActualDecoders(decoder)
  return decoder.table_filter(
      lambda tbl: _AddActualToTable(tbl) if tbl.name in tables
                  else tbl.copy())

def _AddActualToTable(table):
  """Generates a copy of the given table, where the 'actual' field is
     defined by the corresponding (actual) class decoder described in
     the table."""
  return table.row_filter(_AddActualToRow)

def _AddActualToRow(r):
  """Generates a copy of the given row, where (if applicable), the
     'actual' field is defined by the corresponding (actual) class
     decoder described in the table."""
  patterns = list(r.patterns)
  action = r.action.copy()
  if (isinstance(action, dgen_core.DecoderAction) and
      dgen_decoder.ActionDefinesDecoder(action)):
    actual = dgen_decoder.BaselineToActual(action)
    action.define('actual', ActualName(actual))
  row = dgen_core.Row(patterns, action)
  return row

ACTUAL_BASE_H_HEADER="""%(FILE_HEADER)s
#ifndef %(IFDEF_NAME)s
#define %(IFDEF_NAME)s

// Note: actual decoders are spread out over multiple files so that
// they are small enough to be handled by the Rietveld server.
"""

ACTUAL_BASE_INCLUDE=(
"""#include "%(FILEBASE)s_%(filename_index)s.h"
""")

ACTUAL_BASE_H_FOOTER="""
#endif  // %(IFDEF_NAME)s
"""

def generate_actuals_base_h(decoder, decoder_name,
                               filename, out, cl_args):
  """Generates actual decoder C++ declarations in the given file.

    Args:
        decoder: The decoder tables.
        decoder_name: The name of the decoder state to build.
        filename: The (localized) name for the .h file.
        out: a COutput object to write to.
        cl_args: A dictionary of additional command line arguments.
        """
  if not decoder.primary: raise Exception('No tables provided.')

  num_blocks = dgen_output.GetNumberCodeBlocks(cl_args['auto-actual-sep'])

  assert filename.endswith('actuals.h')

  values = {
      'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
      'IFDEF_NAME' : dgen_output.ifdef_name(filename),
      'FILEBASE'   : filename[:-len('.h')],
      'decoder_name': decoder_name,
      }
  out.write(ACTUAL_BASE_H_HEADER % values)
  for block in range(1, num_blocks+1):
    values['filename_index'] = block
    out.write(ACTUAL_BASE_INCLUDE % values)
  out.write(ACTUAL_BASE_H_FOOTER % values)

ACTUAL_H_HEADER="""%(FILE_HEADER)s
#ifndef %(IFDEF_NAME)s
#define %(IFDEF_NAME)s

#include "native_client/src/trusted/validator_arm/inst_classes.h"
#include "native_client/src/trusted/validator_arm/arm_helpers.h"

namespace nacl_arm_dec {
"""

ACTUAL_H_FOOTER="""
} // namespace nacl_arm_test

#endif  // %(IFDEF_NAME)s
"""

def generate_actuals_h(decoder, decoder_name, filename, out, cl_args):
  """Generates actual decoder C++ declarations in the given file.

    Args:
        decoder: The decoder tables.
        decoder_name: The name of the decoder state to build.
        filename: The (localized) name for the .h file.
        out: a COutput object to write to.
        cl_args: A dictionary of additional command line arguments.
        """
  if not decoder.primary: raise Exception('No tables provided.')

  separators = cl_args['auto-actual-sep']
  num_blocks = dgen_output.GetNumberCodeBlocks(separators)

  # Find block to print
  block = dgen_output.FindBlockIndex(filename, 'actuals_%s.h', num_blocks)

  values = {
      'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
      'IFDEF_NAME' : dgen_output.ifdef_name(filename),
      'decoder_name': decoder_name,
      }
  out.write(ACTUAL_H_HEADER % values)
  _print_actual_headers(
    GetActualDecodersBlock(decoder, block, separators), out)
  out.write(ACTUAL_H_FOOTER % values)

ACTUAL_CC_HEADER="""%(FILE_HEADER)s
#include "native_client/src/trusted/validator_arm/inst_classes.h"
#include "native_client/src/trusted/validator_arm/gen/arm32_decode_actuals.h"

namespace nacl_arm_dec {
"""

ACTUAL_CC_FOOTER="""
}  // namespace nacl_arm_dec
"""

def generate_actuals_cc(decoder, decoder_name, filename, out, cl_args):
  """Generates the actual decoder C++ definitions in the given file.

    Args:
        decoder: The decoder tables.
        decoder_name: The name of the decoder state to build.
        filename: The (localized) name for the .h file.
        out: a COutput object to write to.
        cl_args: A dictionary of additional command line arguments.