#! /usr/bin/env perl

#
# This file is part of libFirm.
# Copyright (C) 2014 University of Karlsruhe.
#

# This script generates C code which emits assembler code for the
# assembler ir nodes. It takes a "emit" key from the node specification
# and generates ${arch}_emitf() calls for them.

use strict;
use warnings;

our $specfile   = $ARGV[0];
our $target_dir = $ARGV[1];

our $arch;
our %nodes;

unless (my $return = do "${specfile}") {
	die "Fatal error: couldn't parse $specfile: $@" if $@;
	die "Fatal error: couldn't do $specfile: $!"    unless defined $return;
	die "Fatal error: couldn't run $specfile"       unless $return;
}

# buffers for output
my $obst_func            = ""; # buffer for the emit functions
my $obst_register        = ""; # buffer for emitter register code
my $obst_register_binary = ""; # buffer for emitter register code

foreach my $op (sort(keys(%nodes))) {
	my $n = $nodes{$op};

	my $emit = $n->{emit};
	if (!defined($emit) && defined($n->{template})) {
		$emit = $n->{template}->{emit};
	}
	if (defined($emit)) {
		my $emit_func;
		if ($emit eq "") {
			$emit_func = "be_emit_nothing";
		} else {
			$emit_func = "emit_${arch}_$op";

			$obst_func .= "static void $emit_func(ir_node const *const node)\n";
			$obst_func .= "{\n";
			my $name = $n->{name} // lc($op);
			$emit =~ s/{name}/$name/g;
			$emit =~ s/\n/\\n/g;
			$obst_func .= "\t${arch}_emitf(node, \"$emit\");\n";
			$obst_func .= "}\n\n";
		}
		$obst_register .= "\tbe_set_emitter(op_${arch}_$op, $emit_func);\n";
	}
	my $encode = $n->{encode};
	if (defined($encode)) {
		my $emit_func;
		if ($encode eq "") {
			$emit_func = "be_emit_nothing";
		} else {
			$emit_func = "enc_${arch}_$op";

			$obst_func .= "static void $emit_func(ir_node const *const node)\n";
			$obst_func .= "{\n";
			$obst_func .= "\t(void)node; /* avoid potential warning */\n";
			$obst_func .= "\t${encode};\n";
			$obst_func .= "}\n\n";
		}
		$obst_register_binary .= "\tbe_set_emitter(op_${arch}_$op, $emit_func);\n";
	}
}

my $creation_time = localtime(time());

sub create_with_header
{
	my ($name, $brief) = @_;

	open(my $out, ">", $name) // die("Could not open $name, reason: $!\n");
	print $out <<EOF;
/**
 * \@file
 * \@brief $brief
 * \@note  DO NOT EDIT THIS FILE, your changes will be lost.
 *         Edit $specfile instead.
 *         created by: $0 $specfile $target_dir
 * \@date  $creation_time
 */
EOF
	return $out;
}

my $out_h = create_with_header("$target_dir/gen_${arch}_emitter.h", "Function prototypes for the emitter functions.");
my $uarch = uc($arch);
print $out_h <<EOF;
#ifndef FIRM_BE_${uarch}_GEN_${uarch}_EMITTER_H
#define FIRM_BE_${uarch}_GEN_${uarch}_EMITTER_H

void ${arch}_register_spec_emitters(void);
void ${arch}_register_spec_binary_emitters(void);

#endif
EOF
close($out_h);

my $out_c = create_with_header("$target_dir/gen_${arch}_emitter.c", "Generated functions to emit code for assembler ir nodes.");
print $out_c <<EOF;
#include "gen_${arch}_emitter.h"

#include "beemithlp.h"
#include "gen_${arch}_new_nodes.h"
#include "${arch}_emitter.h"

${obst_func}

void ${arch}_register_spec_emitters(void)
{
${obst_register}
}

void ${arch}_register_spec_binary_emitters(void)
{
${obst_register_binary}
}
EOF
close($out_c);
