The following are some useful hints on how to handle expressions.
First, you should be careful about typing in expressions. If you take a symbol address, the result type should be a pointer to the type of the symbol you are addressing. If you do a load, the result type should be one pointer less than the expression you are loading. Stores should have one less reference on the right hand side than the left. For example, in i=j, the type of the left hand side is a pointer to int while the type of the right hand side is an int.
Array indexing operations must be performed on pointers to arrays, not on pointers to element types. If the source code includes indexing of element types, the front end casts it to an array type.
Because subexpressions are owned by the expressions that contain them, you can never share an expression node. To do common subexpression elimination, you must store the value in a temporary and load from the temporary. For constants, you do not want to do this since it loses the const-ness of the expression. If you look in expression_utils.h in the directory nci/suif/suif2b/utils you will find some useful utilities for manipulating constant expressions.
Finally, when you do store an expression into a temporary, don't give the temporary a name! Leave it anonymous. There is a pass that will add names, should you need them, after compilation has completed, and it will make sure the names do not clash with any other name. This maintains the maximum flexibility. If you invent an identifier, you either have to invent something which is lexically invalid in your source language, which will cause problems if you later try to output the code as source, or you have to produce an identifier which could conflict with a yet-to-be-seen identifier in the source.