/*
* Copyright (c) 1995, 1996, 1997, 1998 The University of Utah and
* the Computer Systems Laboratory at the University of Utah (CSL).
*
* This file is part of Flick, the Flexible IDL Compiler Kit.
*
* Flick is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Flick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Flick; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
*/
#include <assert.h>
#include <string.h>
#include <mom/libmint.h>
#include <mom/c/libcast.h>
#include <mom/c/pbe.hh>
/* This function is an auxiliary for `mu_inline_struct_union_functor::func',
below. Given a MINT constant, this function creates and returns an
equivalent CAST expression. */
cast_expr make_case_value_expr(mint_const mint_literal)
{
cast_expr cast_literal;
switch (mint_literal->kind) {
case MINT_CONST_INT:
switch (mint_literal->mint_const_u_u.const_int.kind) {
case MINT_CONST_LITERAL:
cast_literal =
cast_new_expr_lit_int((mint_literal->
mint_const_u_u.
const_int.
mint_const_int_u_u.
value),
0);
break;
case MINT_CONST_SYMBOLIC:
cast_literal =
cast_new_expr_name(mint_literal->
mint_const_u_u.const_int.
mint_const_int_u_u.name);
break;
default:
panic("Unknown MINT constant category.");
break;
}
break;
case MINT_CONST_CHAR:
switch (mint_literal->mint_const_u_u.const_char.kind) {
case MINT_CONST_LITERAL:
cast_literal =
cast_new_expr_lit_char((mint_literal->
mint_const_u_u.
const_char.
mint_const_char_u_u.
value),
0);
break;
case MINT_CONST_SYMBOLIC:
cast_literal =
cast_new_expr_name(mint_literal->
mint_const_u_u.const_char.
mint_const_char_u_u.name);
break;
default:
panic("Unknown MINT constant category.");
break;
}
break;
default:
panic("MINT constant type %d not supported as discrimintor.",
mint_literal->kind);
}
return cast_literal;
}
/*****************************************************************************/
/* This functor is called by `mu_union_case' to generate the body of a `case'
within a `switch' that marshals/unmarshals a single variant of a "struct
union."
The marshaled/unmarshaled data is/will be stored in the location named by
`slot_expr' (a CAST expression that names a member of the CAST union). The
type of that slot is described by `slot_ctype'. The MINT type of the data
is described by `slot_itype', and `slot_mapping' describes the association
between the CAST and the MINT representations of the data to be marshaled or
unmarshaled.
*/
struct mu_inline_struct_union_case_functor : public functor
{
virtual void func(mu_state *must);
/*****/
mu_inline_struct_union_case_functor() {}
mu_inline_struct_union_case_functor(cast_expr expr,
cast_type ctype,
mint_ref itype,
pres_c_mapping mapping) :
slot_expr(expr),
slot_ctype(ctype),
slot_itype(itype),
slot_mapping(mapping) {}
/*****/
cast_expr slot_expr;
cast_type slot_ctype;
mint_ref slot_itype;
pres_c_mapping slot_mapping;
};
void mu_inline_struct_union_case_functor::func(mu_state *must)
{
must->mu_mapping(slot_expr, slot_ctype, slot_itype, slot_mapping);
/* Break any chunk that may be open at this point. */
must->break_chunk();
}
/*****************************************************************************/
/* This functor is called by `mu_union_case' to generate the body of a `case'
within a `switch' that marshals/unmarshals a single variant of a "struct
union." This functor is different the previous one, however, in that this
functor *discards* the marshaled/unmarshaled data. Void union variants are
handled by this functor. */
struct mu_inline_struct_union_discard_functor : public functor
{
virtual void func(mu_state *must);
/*****/
mu_inline_struct_union_discard_functor() {}
mu_inline_struct_union_discard_functor(mint_ref itype,
pres_c_mapping mapping) :
discard_itype(itype),
discard_mapping(mapping) {}
/*****/
mint_ref discard_itype;
pres_c_mapping discard_mapping;
};
void mu_inline_struct_union_discard_functor::func(mu_state *must)
{
/* I suppose that in general, we should be able to discard any kind
of data. But currently, when we have nowhere to store the data,
`mu_mapping' works only when we are discarding a `void' (MINT_VOID)
via a direct mapping (PRES_C_MAPPING_DIRECT). */
mint_def *discard_def = &(must->
pres->mint.defs.defs_val[discard_itype]);
if (discard_def->kind != MINT_VOID)
panic("Cannot yet discard a non-void union variant.");
/* Rather than risk our luck by calling `mu_mapping' with NULL
pointers, just do nothing. When we can discard arbitrary data this
should be changed. */
/* must->mu_mapping(0, 0, discard_itype, discard_mapping); */
/* Break any chunk that may be open at this point. */
must->break_chunk();
}
/*****************************************************************************/
/* This functor is called by `mu_union_case' to generate the body of an error-
producing default `case' within a `switch' that marshals/unmarshals a
"struct union." */
void mu_inline_struct_union_error_functor::func(mu_state *must)
{
/* NOTE: Breaking a glob here would cause an excessive
number of globs in the common case. It is not
necessary since the error macros have *no*
assumptions about the marshal/unmarshal state. */
must->add_stmt(must->make_error(FLICK_ERROR_STRUCT_UNION));
/* Break any chunk that may be open at this point. */
must->break_chunk();
}
/*****************************************************************************/
/* This functor is called by `mu_union' in order to generate the code that
marshals/unmarshals the variants of a "struct union." (The discriminator of
the "struct union" is handled separately, before this functor is invoked.)
*/
struct mu_inline_struct_union_functor : public functor
{
virtual void func(mu_state *must);
mu_inline_struct_union_functor(inline_state *ist_in,
mint_ref union_itype_in,
pres_c_inline inl_in) :
ist(ist_in),
union_itype(union_itype_in),
inl(inl_in) {}
inline_state *ist;
mint_ref union_itype;
pres_c_inline inl;
};
/* This method handles a PRES_C_INLINE_STRUCT_UNION inline presentation node,
which always corresponds to a MINT_UNION node on the interface (itype) side.
This method produces the C `switch' statement that processes the variants of
the union. Each case within the switch contains code to marshal/unmarshal a
particular variant. Note that by the time we execute this function, the C
code to marshal or unmarshal the union discriminator has already been output
by `mu_inline_struct_union'.
*/
void mu_inline_struct_union_functor::func(mu_state *must)
{
pres_c_inline_struct_union *inlsu;
mint_union_def *udef;
int i;
mu_memory_allocator_state *enter_case_state;
mint_const discrim_val;
cast_expr discrim_expr, union_expr, slot_expr, case_value_expr;
cast_type discrim_ctype, union_ctype, slot_ctype;
cast_scoped_name slot_name;
mint_ref slot_itype;
int slot_union_ctype_index;
mu_inline_struct_union_case_functor case_functor;
mu_inline_struct_union_discard_functor discard_functor;
mu_inline_struct_union_error_functor error_functor;
cast_stmt saved_c_block, switch_body_c_block, case_c_block;
mu_msg_span *union_span = 0, *parent_span = 0;
/*****/
assert(inl->kind == PRES_C_INLINE_STRUCT_UNION);
inlsu = &(inl->pres_c_inline_u_u.struct_union);
/* Find the itype and make sure it matches. */
assert(must->pres->mint.defs.defs_val[union_itype].kind == MINT_UNION);
udef = &(must->
pres->mint.defs.defs_val[union_itype].mint_def_u.union_def);
/* At one time, we marshaled/unmarshaled the discriminator here. But
now we process the discriminator in `mu_inline_struct_union' because
at that point we are *outside* of `mu_union'. The union-generation
code analyzes memory usage in order to determine if any code path
must break a glob. This analysis is effectively defeated when the
processing of the discriminator breaks the glob --- the result in
that case is we can't share a single glob for all arms of our
`switch'. By handling the discriminator before we enter `mu_union',
`mu_union' can analyze memory usage across the `switch' alone.
(How can the discriminator itself cause a glob break? If it is
handled my a marshal/unmarshal stub. Currently, we always break the
current lgob before calling a marshal or unmarshal stub, and the
stub always leaves us in a state without a current glob.)
We still need to get the `discrim_expr', however, so that we can
reference it in our `switch' statement. */
ist->slot_access(inlsu->discrim.index, &discrim_expr, &discrim_ctype);
/* Break the current chunk and save the state of the glob/chunk memory
allocator. Each arm of our `switch' statement will start in a state
that has no current chunk. (In particular, each `switch' arm will
begin in the `enter_case_state'. Each `switch' arm will result in a
state that has no current chunk; this is ensured by the functors we
give to `mu_union_case'. Higher-level glob management is the domain
of `mu_union' and `mu_union_case'.) */
must->break_chunk();
enter_case_state = must->memory_allocator_state();
/* If an abort were to occur after this union then we need to
try and rollback anything done in the union. So we construct
an abort control block that does a switch and then for each
case we make a thread block and then add it to this
control block. */
struct mu_abort_block *mab_par, *mab_con, *mab_thr;
mab_con = new mu_abort_block();
mab_par = must->abort_block;
mab_con->set_kind(MABK_CONTROL_SWITCH);
mab_con->set_expr(discrim_expr);
mab_con->begin();
/* After marshaling/unmarshaling the discriminator, we must produce a
`switch' statement that will take us to the marshal/unmarshal code
for the selected variant of the union. Because many of our methods
use `add_stmt' to build code, we must manipulate `c_block' in order
to build different pieces of the code. */
saved_c_block = must->c_block;
must->c_block = cast_new_block(0, 0);
/* Get `union_expr', which is the CAST expression that accesses the
slot containing our union, and `union_ctype', which is the CAST
type of that union. We need these things in order to access the
members of the union. */
ist->slot_access(inlsu->union_index, &union_expr, &union_ctype);
if( must->current_span ) {
union_span = new mu_msg_span;
union_span->set_kind(MSK_UNION);
union_span->set_block(saved_c_block);
union_span->set_abort(must->abort_block);
union_span->begin();
parent_span = must->current_span;
}
/* Process the cases. */
for (i = 0; i < (signed int)udef->cases.cases_len; ++i) {
/* There is a variance in msg length needed so we
need to add a span for each case */
if( must->current_span ) {
must->current_span = new mu_msg_span;
must->current_span->set_kind(MSK_SEQUENTIAL);
must->current_span->set_block(must->c_block);
must->current_span->set_abort(must->abort_block);
must->current_span->begin();
}
/* Save the body of the `switch' statement we're generating,
and start a new block for this case. */
switch_body_c_block = must->c_block;
must->c_block = cast_new_block(0, 0);
/* Reset the state of the glob/chunk management code to the
"enter case" state. */
must->set_memory_allocator_state(enter_case_state);
/* Create a CAST version of the selector for this case. */
discrim_val = udef->cases.cases_val[i].val;
case_value_expr = make_case_value_expr(discrim_val);
/* Add the case for this child to the parent block
and then add a child to catch the code */
mab_thr = new mu_abort_block();
must->abort_block = mab_thr;
mab_thr->set_kind(MABK_THREAD);
mab_thr->set_expr(case_value_expr);
mab_thr->begin();
/* Now determine the things we need to know about the slot
of the union that corresponds to this case. */
/* Determine `slot_itype'. */
slot_itype = udef->cases.cases_val[i].var;
/* The cases within `inlsu' tell us how the MINT cases in
`udef' correspond to the members of the `union_ctype'.
For each MINT case, the `inlsu' contains the index of the
slot in the `union_ctype' that we should reference. This
index may be -1, which means that the MINT case doesn't map
to any member of the CAST union. This is how void cases
are handled. */
slot_union_ctype_index = inlsu->cases.cases_val[i].index;
if (slot_union_ctype_index != -1) {
/* Dig the `slot_name' and `slot_ctype' for this case
out of the `union_ctype'. */
slot_name =
union_ctype->cast_type_u_u.agg_type.
scope.cast_scope_val[slot_union_ctype_index].
name;
slot_ctype = union_ctype->cast_type_u_u.agg_type.
scope.cast_scope_val[slot_union_ctype_index].
u.cast_def_u_u.var_def.type;
/* Determine `slot_expr'. */
slot_expr = cast_new_expr_sel(union_expr, slot_name);
/* Next, marshal or unmarshal this case. This will
start a new chunk automatically, since we haven't
marshaled/unmarshaled anything since calling
`break_chunk'. */
case_functor.slot_expr = slot_expr;
case_functor.slot_ctype = slot_ctype;
case_functor.slot_itype = slot_itype;
case_functor.slot_mapping = inlsu->
cases.cases_val[i].mapping;
must->mu_union_case(&case_functor);
} else {
/* We must invoke `mu_union_case' even for cases that
don't map to anything in the CAST union. Most
importantly, this lets `mu_union_case' insert any
glob/chunk management code that it needs to add.
Secondarily, it gives us a chance to discard junk
data in the message. (Of course, if `slot_itype' is
MINT_VOID, there's nothing to discard.) */
slot_expr = 0;
discard_functor.discard_itype = slot_itype;
discard_functor.discard_mapping = inlsu->
cases.cases_val[i].
mapping;
must->mu_union_case(&discard_functor);
}
/* Finally, append a `break' to the block for this case, and
add this case to the body of our `switch' statement. */
must->add_stmt(cast_new_break());
mab_thr->end();
mab_con->add_child(mab_thr, MABF_OUT_OF_LINE);
must->add_stmt(mab_thr->get_block_label());
case_c_block = must->c_block;
must->c_block = switch_body_c_block;
must->add_stmt(cast_new_case(case_value_expr, case_c_block));
if( parent_span ) {
must->current_span->end();
union_span->add_child(must->current_span);
}
}
/* Process the default case. */
switch_body_c_block = must->c_block;
must->c_block = cast_new_block(0, 0);
mab_thr = new mu_abort_block();
must->abort_block = mab_thr;
mab_thr->set_kind(MABK_THREAD);
mab_thr->begin();
must->set_memory_allocator_state(enter_case_state);
if (inlsu->dfault) {
/* -1 is the magic value that indictates "no default." */
assert(udef->dfault != -1);
/* Determine `slot_itype'. */
slot_itype = udef->dfault;
slot_union_ctype_index = inlsu->dfault->index;
if (slot_union_ctype_index != -1) {
if( must->current_span ) {
must->current_span = new mu_msg_span;
must->current_span->set_kind(MSK_SEQUENTIAL);
must->current_span->set_block(must->c_block);
must->current_span->set_abort(must->
abort_block);
must->current_span->begin();
}
/* Dig the `slot_name' and `slot_ctype' for this case
out of the `union_ctype'. */
slot_name =
union_ctype->cast_type_u_u.agg_type.
scope.cast_scope_val[slot_union_ctype_index].
name;
slot_ctype = union_ctype->cast_type_u_u.agg_type.
scope.cast_scope_val[slot_union_ctype_index].
u.cast_def_u_u.var_def.type;
/* Determine `slot_expr'. */
slot_expr = cast_new_expr_sel(union_expr, slot_name);
/* Marshal or unmarshal this case. */
case_functor.slot_expr = slot_expr;
case_functor.slot_ctype = slot_ctype;
case_functor.slot_itype = slot_itype;
case_functor.slot_mapping = inlsu->dfault->mapping;
must->mu_union_case(&case_functor);
if( parent_span ) {
must->current_span->end();
union_span->add_child(must->current_span);
}
} else {
/* We must invoke `mu_union_case' even for cases that
don't map to anything in the CAST union. */
discard_functor.discard_itype = slot_itype;
discard_functor.discard_mapping = inlsu->
dfault->mapping;
must->mu_union_case(&discard_functor);
}
} else {
/* There is no default case of our union. Emit a statement in
the default case of our `switch' statement that will handle
erroneous discriminator values. */
must->mu_union_case(&error_functor);
}
/* Append a `break' to the block for this case, and add this case to
the body of our `switch' statement. */
if( parent_span ) {
union_span->end();
parent_span->add_child(union_span);
must->current_span = parent_span;
}
must->add_stmt(cast_new_break());
mab_thr->end();
mab_con->add_child(mab_thr, MABF_OUT_OF_LINE);
must->add_stmt(mab_thr->get_block_label());
case_c_block = must->c_block;
must->c_block = switch_body_c_block;
must->add_stmt(cast_new_default(case_c_block));
mab_con->end();
must->add_stmt(mab_con->get_block_label());
mab_par->add_child(mab_con, MABF_OUT_OF_LINE);
/* Produce the complete `switch' statement. */
switch_body_c_block = must->c_block;
must->c_block = saved_c_block;
must->add_stmt(cast_new_switch(discrim_expr, switch_body_c_block));
must->abort_block = mab_par;
/* Finally, clean up. */
delete enter_case_state;
}
/*****************************************************************************/
/* Finally, the `mu_state' method that sets the code generation in motion. */
void mu_state::mu_inline_struct_union(inline_state *ist,
mint_ref union_itype,
pres_c_inline inl)
{
pres_c_inline_struct_union *inlsu;
mint_union_def *udef;
cast_expr discrim_expr;
cast_type discrim_ctype;
mu_inline_struct_union_functor f(ist, union_itype, inl);
/*****/
assert(inl->kind == PRES_C_INLINE_STRUCT_UNION);
inlsu = &(inl->pres_c_inline_u_u.struct_union);
/* Find the itype and make sure it matches. */
assert(pres->mint.defs.defs_val[union_itype].kind == MINT_UNION);
udef = &(pres->mint.defs.defs_val[union_itype].mint_def_u.union_def);
/* Marshal or unmarshal the discriminator. The code to do this is
similar to `mu_inline_atom', except that the index is determined
differently. */
ist->slot_access(inlsu->discrim.index, &discrim_expr, &discrim_ctype);
mu_mapping(discrim_expr, discrim_ctype, udef->discrim,
inlsu->discrim.mapping);
/* Finally, let `mu_union' and our functor generate the big `switch'
statement. */
mu_union(&f);
}
/* End of file. */
syntax highlighted by Code2HTML, v. 0.9.1