-- (C) Copyright International Business Machines Corporation 23 January 
-- 1990.  All Rights Reserved. 
--  
-- See the file USERAGREEMENT distributed with this software for full 
-- terms and conditions of use. 
-- File: cgconvert.pp
-- Author: Andy Lowry
-- SCCS Info: @(#)cgconvert.pp	1.5 3/13/90

-- This module translates a 'convert' statement into the appropriate
-- conversion instruction.  Any of three different primitive type
-- classes may be involved: integer, real, and ordered enumeration.
-- For each pair of the above, there is a specific LI opcode to do the
-- conversion.  For conversions between operands of the same primitive
-- type, we use the following: int => int is a copy; real => real is
-- not yet implemented; and enum => enum is performed in two steps, as
-- enum => int => enum.

#include "typemark.h"
#include "codegen.h"

cgConvert: using (cgInternal, interpform)

process (Q: cgStmtQ)

declare
  args: cgStmt;
  srcTDef: type_definition;
  dstTDef: type_definition;
  op: interpform!operation;
  empty: empty;
begin
  receive args from Q;
  
  -- Get source and destination type definitions
  srcTDef <- type_definition#(FNS.typeDef(typename#(args.cgData.Proc.objType(
	  objectname#(AREF(x,args.stmt.operands,ONE))))));
  dstTDef <- type_definition#(FNS.typeDef(typename#(args.cgData.Proc.objType(
	  objectname#(AREF(x,args.stmt.operands,ZERO))))));

  -- Fill in opcode and qualifier according to operand types
  new op;
  select primitive_types#(case of srcTDef.specification)
  where (primitive_types#'integertype')
    select primitive_types#(case of dstTDef.specification)
    where (primitive_types#'integertype')
      -- int => int, use a simple copy
      op.opcode <- interpform!opcode#'copy';
      unite op.qualifier.empty from empty;
      
    where (primitive_types#'enumerationtype')
      -- int => enum... use icvt_enum instruction, qualifier gives
      -- size of enumeration
      reveal dstTdef.specification.enumeration;
      op.opcode <- interpform!opcode#'icvt_enum';
      unite op.qualifier.integer 
	  from I(size of dstTDef.specification.enumeration.values);

    where (primitive_types#'realtype')
      -- int => real... use icvt_real instruction, no qualifier
      op.opcode <- interpform!opcode#'icvt_real';
      unite op.qualifier.empty from empty;
      
    otherwise
      exit cantHappen;
    end select;
    
  where (primitive_types#'enumerationtype')
    select primitive_types#(case of dstTDef.specification)
    where (primitive_types#'integertype')
      -- enum => int... use oecvt_int instruction, no qualifier
      op.opcode <- interpform!opcode#'oecvt_int';
      unite op.qualifier.empty from empty;
      
    where (primitive_types#'enumerationtype')
      -- enum => enum... code as enum => int => enum, with a compiler
      -- temporary used for the intermediate step.  Note that we must
      -- not fall through out of this case, since the normal final
      -- processing (build an operand list from dest and source
      -- operands and install the instruction) does not apply... we do
      -- complete instruction assembly and installation here.
      reveal dstTDef.specification.enumeration;
      block declare
	tmp: interpform!operand;
      begin
	-- allocate temp address
	tmp <- interpform!operand#(args.cgData.Proc.tmpAddr());
	-- enum (src) => int (tmp)
	op.opcode <- interpform!opcode#'oecvt_int';
	unite op.qualifier.empty from empty;
	new op.operands;
	insert interpform!operand#(copy of tmp) into op.operands;
	insert interpform!operand#(args.cgData.Proc.objAddr(
	    objectname#(AREF(x,args.stmt.operands,ONE)))) into op.operands;
	ADDINSTR(op);
	-- int (tmp) => enum (dst)
	new op;
	op.opcode <- interpform!opcode#'icvt_enum';
	unite op.qualifier.integer
	    from I(size of dstTDef.specification.enumeration.values);
	new op.operands;
	insert interpform!operand#(args.cgData.Proc.objAddr(
	    objectname#(AREF(x,args.stmt.operands,ZERO)))) into op.operands;
	insert tmp into op.operands;
	ADDINSTR(op);
	-- Avoid normal final processing
	return args;
	exit done;
      end block;
      
	
    where (primitive_types#'realtype')
      -- enum => real... use oecvt_real instruction, no qualifier
      op.opcode <- interpform!opcode#'oecvt_real';
      unite op.qualifier.empty from empty;
      
    otherwise
      exit cantHappen;
    end select;
    
  where (primitive_types#'realtype')
    select primitive_types#(case of dstTDef.specification)
    where (primitive_types#'integertype')
      -- real => int... use rcvt_int instruction, no qualifier
      op.opcode <- interpform!opcode#'rcvt_int';
      unite op.qualifier.empty from empty;
      
    where (primitive_types#'enumerationtype')
      -- real => enum... use rcvt_enum instruction, qualifier gives
      -- size of enumeration
      reveal dstTDef.specification.enumeration;
      op.opcode <- interpform!opcode#'rcvt_enum';
      unite op.qualifier.integer 
	  from I(size of dstTDef.specification.enumeration.values);
      
    where (primitive_types#'realtype')
      -- real => real... not yet implemented (copy is no good due to
      -- possibility of depeletion caused by accuracy requirements in
      -- destination)
      return args;
      exit NYI;
      
    otherwise
      exit cantHappen;
    end select;
    
  otherwise
    exit cantHappen;
  end select;
  
  -- Most cases drop through to here, where we build an operands list
  -- containing destination and source operand, and ship off the
  -- instruction.
  new op.operands;
  insert interpform!operand#(args.cgData.Proc.objAddr(
      objectname#(AREF(x,args.stmt.operands,ZERO)))) into op.operands;
  insert interpform!operand#(args.cgData.Proc.objAddr(
      objectname#(AREF(x,args.stmt.operands,ONE)))) into op.operands;
  ADDINSTR(op);
  
  return args;
  
on exit(done)
  -- here for cases that don't want the normal final processing
  
on exit (NYI)
  -- here for cases that are not yet handled
  
on exit(cantHappen)
  print S("CantHappen exit taken in cgconvert");
end process
