https://gcc.gnu.org/g:131d80fb55d57b4a25ef3d5e62567955df825d7a

commit r17-3145-g131d80fb55d57b4a25ef3d5e62567955df825d7a
Author: Robert Dubner <[email protected]>
Date:   Fri Aug 7 16:03:26 2026 -0400

    cobol: Refactor COMPUTE using Reverse Polish stack.
    
    COBOL provides the COMPUTE statement, which evaluates an expression and
    assigns the result to one or more target variables.
    
    Our former implementation of COMPUTE z = a * b + c * d was profligate
    with the creation of temporary variables.  In that case, temporaries
    were created for a*b and c*d, and another created for their sum, which
    then became input to the assignment phase.
    
    These changes change that implementation to a version using Reverse
    Polish Notation.  An RPN stack is created and passed to the libgcobol
    library, where the stack is reduced and a result created.  This is
    computionally somewhat more efficient, and it greatly reduces the
    computational load on the GCC middle end.
    
    Co-authored-by: James K. Lowden <[email protected]>
    Co-authored-by: Robert Dubner <[email protected]>
    
    gcc/cobol/ChangeLog:
    
            * genapi.cc (gg_array_of_field_pointers): Modified to return a
            pointer to the first element of the array.
            (gg_array_of_uchar_p): Formatting.
            (parser_sort): Use updated gg_array_of_field_pointers().
            (parser_file_sort): Likewise.
            (parser_file_merge): Likewise.
            * genapi.h (parser_compute): New declarations.
            * gengen.h (gg_array_of_field_pointers): Modified declaration.
            * genmath.cc (parser_compute): New declarations.
            * genutil.h (gg_array_of_uchar_p): New declaration.
            * move.cc (mh_binary_to_numdisp): Use new __gg__prohibited()
            routine.
            (parser_move): Formatting.
            (parser_move_multi): Formatting.
            * parse.y: RPN support.
            * parse_ante.h (ast_op): Likewise.
            (ast_relop): Likewise.
            (struct ast_op_t): Likewise.
            (struct refer_list_t): Likewise.
            (struct vargs_t): Likewise.
            * symbols.h (struct rpn_t): Likewise.
            (struct expr_t): Likewise.
    
    libgcobol/ChangeLog:
    
            * gmath.cc (__gg__pow): Use exponentiation_helper().
            (exponentiation_helper): New routine.
            (__gg__process_compute_error): Formatting.
            (multiply_int128_by_int128): Use new int128 structure.
            (divide_int128_by_int128): Formatting.
            (multiply_int256_by_int256): New routine.
            (divide_int256_by_int256): New routine.
            (compute_fixed_add): New routine to implement RPN COMPUTE.
            (compute_fixed_subtract): Likewise.
            (compute_fixed_multiply): Likewise.
            (compute_fixed_divide): Likewise.
            (compute_fixed_negate): Likewise.
            (compute_float_add): Likewise.
            (compute_float_subtract): Likewise.
            (compute_float_multiply): Likewise.
            (compute_float_divide): Likewise.
            (compute_float_pow): Likewise.
            (compute_float_negate): Likewise.
            (__gg__compute_fixed): Likewise.
            (__gg__compute_float): Likewise.
            * libgcobol.cc (__gg__prohibited): New routine tests value and
            target for ROUNDING MODE PROHIBITED compliance.
            * libgcobol.h: Formatting.

Diff:
---
 gcc/cobol/genapi.cc    |  22 +--
 gcc/cobol/genapi.h     |  12 ++
 gcc/cobol/gengen.h     |   2 +-
 gcc/cobol/genmath.cc   | 129 +++++++++++++
 gcc/cobol/genutil.h    |   1 +
 gcc/cobol/move.cc      | 507 ++++++++++++++++++++++++++-----------------------
 gcc/cobol/parse.y      | 270 +++++++++++++++-----------
 gcc/cobol/parse_ante.h | 173 ++++++++++++++++-
 gcc/cobol/symbols.h    |  28 +++
 libgcobol/gmath.cc     | 488 +++++++++++++++++++++++++++++++++++++++++++++--
 libgcobol/libgcobol.cc |  53 ++++++
 libgcobol/libgcobol.h  |   2 +-
 12 files changed, 1294 insertions(+), 393 deletions(-)

diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc
index 418f5ea22f6c..41f46036df98 100644
--- a/gcc/cobol/genapi.cc
+++ b/gcc/cobol/genapi.cc
@@ -11674,12 +11674,11 @@ gg_array_of_field_pointers( const std::vector<const 
cbl_field_t *> &fields )
 
   for( size_t i=0; i<N; i++ )
     {
-    gcc_assert( fields[i] != NULL );
-    gcc_assert( fields[i]->var_decl_node != NULL_TREE );
+    tree field_pointer = fields[i] && fields[i]->var_decl_node
+                       ? gg_get_address_of( fields[i]->var_decl_node)
+                       : null_pointer_node;
 
-    tree field_pointer =
-      gg_cast( cblc_field_p_type_node,
-               gg_get_address_of( fields[i]->var_decl_node ) );
+    field_pointer = gg_cast( cblc_field_p_type_node, field_pointer );
 
     CONSTRUCTOR_APPEND_ELT( elts,
                             bitsize_int( i ),
@@ -11693,7 +11692,7 @@ gg_array_of_field_pointers( const std::vector<const 
cbl_field_t *> &fields )
   TREE_READONLY( retval ) = 1;
   DECL_INITIAL( retval ) = constr;
 
-  return retval;
+  return gg_pointer_to_array(retval);
   }
 
 tree
@@ -11723,9 +11722,7 @@ gg_array_of_uchar_p( const std::vector<tree> &uchar_p )
     }
 
   tree constr = build_constructor( array_type, elts );
-
   tree retval = gg_define_variable( array_type );
-
   TREE_READONLY( retval ) = 1;
   DECL_INITIAL( retval ) = constr;
 
@@ -11788,8 +11785,7 @@ parser_sort(cbl_refer_t tableref,
       }
     }
 
-  tree all_keys = gg_pointer_to_array(
-                     gg_array_of_field_pointers(flattened_fields_2));
+  tree all_keys = gg_array_of_field_pointers(flattened_fields_2);
 
   // Create the array of integers that are the flags for ASCENDING:
   tree ascending = gg_array_of_size_t(flattened_ascending_2 );
@@ -11911,8 +11907,7 @@ parser_file_sort(   cbl_file_t *workfile,
     }
 
   // Create the array of cbl_field_t pointers for the keys
-  tree all_keys = gg_pointer_to_array(
-                     gg_array_of_field_pointers(flattened_fields_2));
+  tree all_keys = gg_array_of_field_pointers(flattened_fields_2);
 
   // Create the array of integers that are the flags for ASCENDING:
   tree ascending = gg_array_of_size_t(flattened_ascending_2 );
@@ -12246,8 +12241,7 @@ parser_file_merge(  cbl_file_t *workfile,
     }
 
   // Create the array of cbl_field_t pointers for the keys
-  tree all_keys = gg_pointer_to_array(
-                     gg_array_of_field_pointers(flattened_fields_2));
+  tree all_keys =  gg_array_of_field_pointers(flattened_fields_2);
 
   // Create the array of integers that are the flags for ASCENDING:
   tree ascending = gg_array_of_size_t(flattened_ascending_2 );
diff --git a/gcc/cobol/genapi.h b/gcc/cobol/genapi.h
index de3825ac2638..0f20f4dfd337 100644
--- a/gcc/cobol/genapi.h
+++ b/gcc/cobol/genapi.h
@@ -204,6 +204,18 @@ parser_classify( struct cbl_field_t *tgt,
            const struct cbl_refer_t &srca,
                  enum                classify_t type );
 
+void
+parser_compute( cbl_refer_t *tgt,
+                const std::deque<rpn_t>& operations,
+                cbl_label_t *lbl );
+
+void
+parser_compute( std::vector<cbl_num_result_t>& results,
+                const std::deque<rpn_t>& operations,
+                cbl_label_t *on_error,
+                cbl_label_t *not_error,
+                cbl_label_t *compute_error);
+
 void
 parser_op( struct cbl_refer_t cref,
            struct cbl_refer_t aref, int op, struct cbl_refer_t bref,
diff --git a/gcc/cobol/gengen.h b/gcc/cobol/gengen.h
index 0f6ba8028592..ea2f2812a3a9 100644
--- a/gcc/cobol/gengen.h
+++ b/gcc/cobol/gengen.h
@@ -508,7 +508,7 @@ extern void gg_free(tree pointer);
 extern tree gg_strlen(tree psz);
 extern size_t gg_sizeof(tree decl_node);
 
-extern tree gg_array_of_field_pointers( const std::vector<cbl_field_t *> 
&fields );
+extern tree gg_array_of_field_pointers( const std::vector<const cbl_field_t *> 
&fields );
 extern tree gg_array_of_bytes( size_t N, unsigned char *values);
 extern tree gg_indirect(tree pointer, tree byte_offset = NULL_TREE);
 extern tree gg_indirect_i(tree pointer, size_t offset=0);
diff --git a/gcc/cobol/genmath.cc b/gcc/cobol/genmath.cc
index 9aab8e711b19..0715b009d23f 100644
--- a/gcc/cobol/genmath.cc
+++ b/gcc/cobol/genmath.cc
@@ -2389,6 +2389,135 @@ parser_divide(  const cbl_refer_t& cref,
                   NULL );
   }
 
+void
+parser_compute( cbl_refer_t *tgt,
+                const std::deque<rpn_t>& operations,
+                cbl_label_t * compute_error_label )
+  {
+  CHECK_FIELD(tgt->field);
+  gcc_assert(operations.size());
+
+  set_up_compute_error_label(compute_error_label);
+
+  gg_assign(var_decl_default_compute_error, integer_zero_node);
+  tree compute_error =    compute_error_label
+                        ? gg_get_address_of( compute_error_label->
+                                             structs.compute_error->
+                                             compute_error_code)
+                        : gg_get_address_of(var_decl_default_compute_error) ;
+
+  SHOW_PARSE
+    {
+    SHOW_PARSE_HEADER
+    SHOW_PARSE_REF(" dest: ", *tgt);
+    char *psz;
+    for(auto op : operations)
+      {
+      if( op.op )
+        {
+        psz = xasprintf("%c", op.op);
+        }
+      else
+        {
+        if( !op.term.field )
+          {
+          psz = xasprintf("both op.op and op.term.field are NULL");
+          }
+        else
+          {
+          psz = xasprintf("%s", op.term.field->name);
+          }
+        }
+
+      SHOW_PARSE_INDENT
+      SHOW_PARSE_TEXT(psz);
+      free(psz);
+      }
+    SHOW_PARSE_END
+    }
+
+  std::string opstring;
+  std::vector<const cbl_field_t *>fields;
+  std::vector<tree> offsets;
+
+  //bool compute_float = false;
+  const char *routine = "__gg__compute_fixed";
+  for(auto op : operations)
+    {
+    if(    op.op == '^'
+        || (op.term.field && op.term.field->type == FldFloat) )
+      {
+      //compute_float = true;
+      routine = "__gg__compute_float";
+      }
+    opstring += op.op ? op.op : ascii_P;
+    fields.push_back(op.term.field);
+    offsets.push_back(refer_offset(op.term));
+    }
+
+  // The final element on the stacks is for the destination field:
+  fields.push_back(tgt->field);
+  offsets.push_back(refer_offset(*tgt));
+
+  tree retval = gg_define_variable(INT);
+  gg_assign(retval,
+            gg_call_expr( INT,
+                          routine,
+                          gg_string_literal(opstring.c_str()),
+                          gg_array_of_field_pointers(fields),
+                          gg_array_of_uchar_p(offsets),
+                          NULL_TREE));
+  gg_assign(gg_indirect(compute_error), retval);
+  }
+
+void
+parser_compute( std::vector<cbl_num_result_t>& results,
+                const std::deque<rpn_t>& operations,
+                cbl_label_t *on_error,
+                cbl_label_t *not_error,
+                cbl_label_t *compute_error)
+  {
+  bool compute_float = false;
+  for(auto op : operations)
+    {
+    if(    op.op == '^'
+        || (op.term.field && op.term.field->type == FldFloat) )
+      {
+      compute_float = true;
+      break;
+      }
+    }
+
+  cbl_field_t *temp_field;
+
+  if( compute_float )
+    {
+    temp_field = new_temporary(FldFloat,
+                               nullptr,
+                               intermediate_e);
+    }
+  else
+    {
+    temp_field = new_temporary(FldNumericBin5,
+                               nullptr,
+                               signable_e);
+    }
+  cbl_refer_t  temp_refer;
+  temp_refer.field = temp_field;
+
+  parser_compute( &temp_refer, operations, compute_error );
+
+  if( !results.empty() )
+    {
+    parser_assign(results.size(),
+                  results.data(),
+                  temp_refer,
+                  on_error,
+                  not_error,
+                  compute_error);
+    }
+  }
+
 void
 parser_op( struct cbl_refer_t cref,
            struct cbl_refer_t aref,
diff --git a/gcc/cobol/genutil.h b/gcc/cobol/genutil.h
index c01e79723593..8eaa86cc062f 100644
--- a/gcc/cobol/genutil.h
+++ b/gcc/cobol/genutil.h
@@ -91,6 +91,7 @@ bool      process_this_exception(const ec_type_t ec);
 void      rt_error(const char *msg);
 tree      build_array_of_size_t( size_t  N,
                                  const size_t *values);
+tree      gg_array_of_uchar_p( const std::vector<tree> &uchar_p );
 void      parser_display_internal_field(tree file_descriptor,
                                         cbl_field_t *field,
                                         bool advance=DISPLAY_NO_ADVANCE);
diff --git a/gcc/cobol/move.cc b/gcc/cobol/move.cc
index 5e49c56c3fd2..bb00cb217155 100644
--- a/gcc/cobol/move.cc
+++ b/gcc/cobol/move.cc
@@ -1293,234 +1293,257 @@ mh_binary_to_numdisp(const cbl_refer_t &destref,
     tree value ;
     get_binary_value(value, sourceref, work_type);
 
-    tree negative = gg_define_variable(INT);
-    gg_assign(negative, integer_zero_node);
-
-    tree sign_location = NULL_TREE;
-
-    if( destref.field->attr & signable_e )
+    tree prohibited = gg_define_variable(INT, integer_zero_node);
+    if( rounded == prohibited_e )
       {
-      sign_location = gg_define_variable(UCHAR_P);
-      if(    (destref.field->attr & separate_e)
-          && (destref.field->attr & leading_e ) )
-        {
-        // separate and leading
-        gg_assign(sign_location, dest_location);
-        gg_increment(dest_location);
-        }
-      else if(    (destref.field->attr & separate_e)
-              && !(destref.field->attr & leading_e ) )
-        {
-        // separate and trailing
-        gg_assign(sign_location, gg_add(dest_location,
-                                        build_int_cst_type(SIZE_T,
-                                         destref.field->data.capacity()-1)));
-        }
-      else if(   !(destref.field->attr & separate_e)
-              &&  (destref.field->attr & leading_e ) )
-        {
-        // internal and leading
-        gg_assign(sign_location, dest_location);
-        }
-      else
-        {
-        // internal and trailing
-        gg_assign(sign_location, gg_add(dest_location,
-                                        build_int_cst_type(SIZE_T,
-                                         destref.field->data.capacity()-1)));
-        }
+      gg_assign(prohibited,
+                gg_call_expr(INT,
+                             "__gg__prohibited",
+                             gg_get_address_of(destref.field->var_decl_node),
+                             value,
+                             NULL_TREE));
       }
-
-    if(    (sourceref.field->attr & signable_e)
-        && (destref.field->attr   & signable_e) )
+    IF(prohibited, eq_op, integer_one_node)
       {
-      // Both source and dest are signable, which means we have to preserve
-      // the source sign and apply it, eventually, to the target.
-      IF( value, lt_op, gg_cast(work_type, integer_zero_node) )
+      set_exception_code(ec_size_truncation_e);
+      if( size_error )
         {
-        gg_assign(negative, integer_one_node);
+        gg_assign(size_error, integer_one_node);
         }
-      ELSE {} ENDIF
       }
+    ELSE
+      {
+      get_binary_value(value, sourceref, work_type);
+      tree negative = gg_define_variable(INT);
+      gg_assign(negative, integer_zero_node);
 
-    // At this point we have to align the source and destination value rdigits.
+      tree sign_location = NULL_TREE;
 
-    if( !(sourceref.field->attr & intermediate_e) )
-      {
-      // Because the source is not intermediate, we can work with the compile-
-      // time values.
-      int source_rdigits = sourceref.field->data.rdigits;
-      int dest_rdigits   = destref.field->data.rdigits;
-      int nshift = source_rdigits - dest_rdigits;
-      if(nshift < 0)
+      if( destref.field->attr & signable_e )
         {
-        // We need to multiply the source by 10^(-nshift) to line them up.
-        FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( -nshift );
-        gg_assign(value, gg_multiply(value,
-                                     wide_int_to_tree(work_type,
-                                                      power_of_ten)));
-        }
-      else if(nshift > 0)
-        {
-        // We need to divide the source by 10^(nshift) to line them up.
-        // This is a potential rounding situation.
-        FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( nshift );
-        tree pot = wide_int_to_tree(work_type, power_of_ten);
-        gg_assign(negative,
-                  gg_bitwise_and( negative,
-                                  round_this_value(value,
-                                                   pot,
-                                                   rounded,
-                                                   size_error)));
+        sign_location = gg_define_variable(UCHAR_P);
+        if(    (destref.field->attr & separate_e)
+            && (destref.field->attr & leading_e ) )
+          {
+          // separate and leading
+          gg_assign(sign_location, dest_location);
+          gg_increment(dest_location);
+          }
+        else if(    (destref.field->attr & separate_e)
+                && !(destref.field->attr & leading_e ) )
+          {
+          // separate and trailing
+          gg_assign(sign_location, gg_add(dest_location,
+                                          build_int_cst_type(SIZE_T,
+                                           destref.field->data.capacity()-1)));
+          }
+        else if(   !(destref.field->attr & separate_e)
+                &&  (destref.field->attr & leading_e ) )
+          {
+          // internal and leading
+          gg_assign(sign_location, dest_location);
+          }
+        else
+          {
+          // internal and trailing
+          gg_assign(sign_location, gg_add(dest_location,
+                                          build_int_cst_type(SIZE_T,
+                                           destref.field->data.capacity()-1)));
+          }
         }
-      }
-    else
-      {
-      // Source is intermediate; we need to use the dynamic source rdigits
-      // Because the source is not intermediate, we can work with the compile-
-      // time values.
-      tree source_rdigits = gg_define_variable(INT);
-      tree dest_rdigits;
-      tree nshift         = gg_define_variable(INT);
 
-      gg_assign(source_rdigits,
-                gg_cast(INT,
-                        member(sourceref.field->var_decl_node,
-                               "rdigits")));
-      dest_rdigits = build_int_cst_type(INT, destref.field->data.rdigits);
-      gg_assign(nshift, gg_subtract(source_rdigits, dest_rdigits));
-      tree power_of_ten = gg_define_variable(work_type);
-      IF( nshift, lt_op, integer_zero_node )
+      if(    (sourceref.field->attr & signable_e)
+          && (destref.field->attr   & signable_e) )
         {
-        // We need to multiply the source by 10^(-nshift) to line them up.
-        gg_assign(power_of_ten,
-                  gg_cast(work_type,
-                          gg_call_expr(INT128,
-                                       "__gg__power_of_ten",
-                                        gg_negate(nshift),
-                                        NULL_TREE)));
-        gg_assign(value, gg_multiply(value, power_of_ten));
+        // Both source and dest are signable, which means we have to preserve
+        // the source sign and apply it, eventually, to the target.
+        IF( value, lt_op, gg_cast(work_type, integer_zero_node) )
+          {
+          gg_assign(negative, integer_one_node);
+          }
+        ELSE {} ENDIF
         }
-      ELSE
+
+      // At this point we have to align the source and destination value 
rdigits.
+
+      if( !(sourceref.field->attr & intermediate_e) )
         {
-        IF( nshift, gt_op, integer_zero_node )
+        // Because the source is not intermediate, we can work with the 
compile-
+        // time values.
+        int source_rdigits = sourceref.field->data.rdigits;
+        int dest_rdigits   = destref.field->data.rdigits;
+        int nshift = source_rdigits - dest_rdigits;
+        if(nshift < 0)
+          {
+          // We need to multiply the source by 10^(-nshift) to line them up.
+          FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( -nshift );
+          gg_assign(value, gg_multiply(value,
+                                       wide_int_to_tree(work_type,
+                                                        power_of_ten)));
+          }
+        else if(nshift > 0)
           {
           // We need to divide the source by 10^(nshift) to line them up.
           // This is a potential rounding situation.
-          gg_assign(power_of_ten,
-                    gg_cast(work_type,
-                            gg_call_expr(INT128,
-                                         "__gg__power_of_ten",
-                                          nshift,
-                                          NULL_TREE)));
+          FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( nshift );
+          tree pot = wide_int_to_tree(work_type, power_of_ten);
           gg_assign(negative,
                     gg_bitwise_and( negative,
                                     round_this_value(value,
-                                                     power_of_ten,
+                                                     pot,
                                                      rounded,
                                                      size_error)));
           }
+        }
+      else
+        {
+        // Source is intermediate; we need to use the dynamic source rdigits
+        // Because the source is not intermediate, we can work with the 
compile-
+        // time values.
+        tree source_rdigits = gg_define_variable(INT);
+        tree dest_rdigits;
+        tree nshift         = gg_define_variable(INT);
+
+        gg_assign(source_rdigits,
+                  gg_cast(INT,
+                          member(sourceref.field->var_decl_node,
+                                 "rdigits")));
+        dest_rdigits = build_int_cst_type(INT, destref.field->data.rdigits);
+        gg_assign(nshift, gg_subtract(source_rdigits, dest_rdigits));
+        tree power_of_ten = gg_define_variable(work_type);
+        IF( nshift, lt_op, integer_zero_node )
+          {
+          // We need to multiply the source by 10^(-nshift) to line them up.
+          gg_assign(power_of_ten,
+                    gg_cast(work_type,
+                            gg_call_expr(INT128,
+                                         "__gg__power_of_ten",
+                                          gg_negate(nshift),
+                                          NULL_TREE)));
+          gg_assign(value, gg_multiply(value, power_of_ten));
+          }
         ELSE
           {
+          IF( nshift, gt_op, integer_zero_node )
+            {
+            // We need to divide the source by 10^(nshift) to line them up.
+            // This is a potential rounding situation.
+            gg_assign(power_of_ten,
+                      gg_cast(work_type,
+                              gg_call_expr(INT128,
+                                           "__gg__power_of_ten",
+                                            nshift,
+                                            NULL_TREE)));
+            gg_assign(negative,
+                      gg_bitwise_and( negative,
+                                      round_this_value(value,
+                                                       power_of_ten,
+                                                       rounded,
+                                                       size_error)));
+            }
+          ELSE
+            {
+            }
+          ENDIF
           }
         ENDIF
         }
-      ENDIF
-      }
 
-    // At this point, value is lined up with the destination.
+      // At this point, value is lined up with the destination.
 
-    // Make it positive
+      // Make it positive
 
-    if( !TYPE_UNSIGNED(work_type) )
-      {
-      gg_assign(value, gg_abs(value));
-      }
+      if( !TYPE_UNSIGNED(work_type) )
+        {
+        gg_assign(value, gg_abs(value));
+        }
 
-    if( size_error )
-      {
-      // We need to see if is too big to fit
-      FIXED_WIDE_INT(128) power_of_ten =
-                                get_power_of_ten(destref.field->data.digits);
-      tree pot = wide_int_to_tree(work_type, power_of_ten);
-      IF( gg_divide(value, pot),
-          ne_op,
-          gg_cast(work_type, integer_zero_node) )
-          {
-          // The value is too big; flag it:
-          gg_assign(size_error, integer_one_node);
-          }
-        ELSE
-          {
-          }
-        ENDIF
-      }
+      if( size_error )
+        {
+        // We need to see if is too big to fit
+        FIXED_WIDE_INT(128) power_of_ten =
+                                  get_power_of_ten(destref.field->data.digits);
+        tree pot = wide_int_to_tree(work_type, power_of_ten);
+        IF( gg_divide(value, pot),
+            ne_op,
+            gg_cast(work_type, integer_zero_node) )
+            {
+            // The value is too big; flag it:
+            gg_assign(size_error, integer_one_node);
+            }
+          ELSE
+            {
+            }
+          ENDIF
+        }
 
-    if( charmap_dest->is_like_ebcdic() )
-      {
-      gg_call(INT,
-              "__gg__binary_to_string_ebcdic",
-              dest_location,
-              build_int_cst_type(INT, destref.field->data.digits),
-              gg_cast(INT128, value),
-              NULL_TREE);
-      }
-    else
-      {
-      gg_call(INT,
-              "__gg__binary_to_string_ascii",
-              dest_location,
-              build_int_cst_type(INT, destref.field->data.digits),
-              gg_cast(INT128, value),
-              NULL_TREE);
-      }
+      if( charmap_dest->is_like_ebcdic() )
+        {
+        gg_call(INT,
+                "__gg__binary_to_string_ebcdic",
+                dest_location,
+                build_int_cst_type(INT, destref.field->data.digits),
+                gg_cast(INT128, value),
+                NULL_TREE);
+        }
+      else
+        {
+        gg_call(INT,
+                "__gg__binary_to_string_ascii",
+                dest_location,
+                build_int_cst_type(INT, destref.field->data.digits),
+                gg_cast(INT128, value),
+                NULL_TREE);
+        }
 
-    if(    (sourceref.field->attr & signable_e )
-        && (destref.field->attr   & signable_e ) )
-      {
-      IF( negative, ne_op, integer_zero_node )
+      if(    (sourceref.field->attr & signable_e )
+          && (destref.field->attr   & signable_e ) )
         {
-        if( destref.field->attr & separate_e )
-          {
-          // We flag the separate as negative
-          gg_assign(gg_indirect(sign_location), minus);
-          }
-        else
+        IF( negative, ne_op, integer_zero_node )
           {
-          if( charmap_dest->is_like_ebcdic() )
+          if( destref.field->attr & separate_e )
             {
-            gg_assign(gg_indirect(sign_location),
-                      gg_bitwise_and(gg_indirect(sign_location),
-                                     build_int_cst_type(UCHAR, 0xDF)));
+            // We flag the separate as negative
+            gg_assign(gg_indirect(sign_location), minus);
             }
           else
             {
-            gg_assign(gg_indirect(sign_location),
-                      gg_bitwise_or(gg_indirect(sign_location),
-                                    build_int_cst_type(UCHAR, 0x70)));
+            if( charmap_dest->is_like_ebcdic() )
+              {
+              gg_assign(gg_indirect(sign_location),
+                        gg_bitwise_and(gg_indirect(sign_location),
+                                       build_int_cst_type(UCHAR, 0xDF)));
+              }
+            else
+              {
+              gg_assign(gg_indirect(sign_location),
+                        gg_bitwise_or(gg_indirect(sign_location),
+                                      build_int_cst_type(UCHAR, 0x70)));
+              }
             }
           }
-        }
-      ELSE
-        {
-        // The result is positive
-        if( destref.field->attr & separate_e )
+        ELSE
           {
-          // We flag the separate as negative
-          gg_assign(gg_indirect(sign_location), plus);
+          // The result is positive
+          if( destref.field->attr & separate_e )
+            {
+            // We flag the separate as negative
+            gg_assign(gg_indirect(sign_location), plus);
+            }
           }
+        ENDIF
+        }
+      else if(   (destref.field->attr & signable_e )
+              && (destref.field->attr & separate_e ) )
+        {
+        // The source is not signed, but the destination is signable and
+        // separate:
+        gg_assign(gg_indirect(sign_location), plus);
         }
-      ENDIF
-      }
-    else if(   (destref.field->attr & signable_e )
-            && (destref.field->attr & separate_e ) )
-      {
-      // The source is not signed, but the destination is signable and
-      // separate:
-      gg_assign(gg_indirect(sign_location), plus);
-      }
 
-    moved = true;
+      moved = true;
+      }
+    ENDIF
     }
 
   return moved;
@@ -3768,36 +3791,36 @@ parser_move(cbl_refer_t destref,
       SHOW_PARSE_REF(" ", sourceref)
       }
     SHOW_PARSE_REF(" TO ", destref)
-      switch(rounded)
-        {
-        case away_from_zero_e:
-          SHOW_PARSE_TEXT(" AWAY_FROM_ZERO")
-          break;
-        case nearest_toward_zero_e:
-          SHOW_PARSE_TEXT(" NEAREST_TOWARD_ZERO")
-          break;
-        case toward_greater_e:
-          SHOW_PARSE_TEXT(" TOWARD_GREATER")
-          break;
-        case toward_lesser_e:
-          SHOW_PARSE_TEXT(" TOWARD_LESSER")
-          break;
-        case nearest_away_from_zero_e:
-          SHOW_PARSE_TEXT(" NEAREST_AWAY_FROM_ZERO")
-          break;
-        case nearest_even_e:
-          SHOW_PARSE_TEXT(" NEAREST_EVEN")
-          break;
-        case prohibited_e:
-          SHOW_PARSE_TEXT(" PROHIBITED")
-          break;
-        case truncation_e:
-          SHOW_PARSE_TEXT(" TRUNCATED")
-          break;
-        default:
-          gcc_unreachable();
-          break;
-        }
+    switch(rounded)
+      {
+      case away_from_zero_e:
+        SHOW_PARSE_TEXT(" AWAY_FROM_ZERO")
+        break;
+      case nearest_toward_zero_e:
+        SHOW_PARSE_TEXT(" NEAREST_TOWARD_ZERO")
+        break;
+      case toward_greater_e:
+        SHOW_PARSE_TEXT(" TOWARD_GREATER")
+        break;
+      case toward_lesser_e:
+        SHOW_PARSE_TEXT(" TOWARD_LESSER")
+        break;
+      case nearest_away_from_zero_e:
+        SHOW_PARSE_TEXT(" NEAREST_AWAY_FROM_ZERO")
+        break;
+      case nearest_even_e:
+        SHOW_PARSE_TEXT(" NEAREST_EVEN")
+        break;
+      case prohibited_e:
+        SHOW_PARSE_TEXT(" PROHIBITED")
+        break;
+      case truncation_e:
+        SHOW_PARSE_TEXT(" TRUNCATED")
+        break;
+      default:
+        gcc_unreachable();
+        break;
+      }
     SHOW_PARSE_END
     }
 
@@ -3867,36 +3890,36 @@ parser_move_multi(cbl_refer_t destref,
       SHOW_PARSE_REF(" ", sourceref)
       }
     SHOW_PARSE_REF(" TO ", destref)
-      switch(rounded)
-        {
-        case away_from_zero_e:
-          SHOW_PARSE_TEXT(" AWAY_FROM_ZERO")
-          break;
-        case nearest_toward_zero_e:
-          SHOW_PARSE_TEXT(" NEAREST_TOWARD_ZERO")
-          break;
-        case toward_greater_e:
-          SHOW_PARSE_TEXT(" TOWARD_GREATER")
-          break;
-        case toward_lesser_e:
-          SHOW_PARSE_TEXT(" TOWARD_LESSER")
-          break;
-        case nearest_away_from_zero_e:
-          SHOW_PARSE_TEXT(" NEAREST_AWAY_FROM_ZERO")
-          break;
-        case nearest_even_e:
-          SHOW_PARSE_TEXT(" NEAREST_EVEN")
-          break;
-        case prohibited_e:
-          SHOW_PARSE_TEXT(" PROHIBITED")
-          break;
-        case truncation_e:
-          SHOW_PARSE_TEXT(" TRUNCATED")
-          break;
-        default:
-          gcc_unreachable();
-          break;
-        }
+    switch(rounded)
+      {
+      case away_from_zero_e:
+        SHOW_PARSE_TEXT(" AWAY_FROM_ZERO")
+        break;
+      case nearest_toward_zero_e:
+        SHOW_PARSE_TEXT(" NEAREST_TOWARD_ZERO")
+        break;
+      case toward_greater_e:
+        SHOW_PARSE_TEXT(" TOWARD_GREATER")
+        break;
+      case toward_lesser_e:
+        SHOW_PARSE_TEXT(" TOWARD_LESSER")
+        break;
+      case nearest_away_from_zero_e:
+        SHOW_PARSE_TEXT(" NEAREST_AWAY_FROM_ZERO")
+        break;
+      case nearest_even_e:
+        SHOW_PARSE_TEXT(" NEAREST_EVEN")
+        break;
+      case prohibited_e:
+        SHOW_PARSE_TEXT(" PROHIBITED")
+        break;
+      case truncation_e:
+        SHOW_PARSE_TEXT(" TRUNCATED")
+        break;
+      default:
+        gcc_unreachable();
+        break;
+      }
     SHOW_PARSE_END
     }
 
diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y
index 77eddebc2267..e940e86fae51 100644
--- a/gcc/cobol/parse.y
+++ b/gcc/cobol/parse.y
@@ -51,6 +51,8 @@
     accept_envar_e,
   };
 
+  class ast_op_t;
+
   struct coll_alphanat_t {
     const char *alpha, *national; 
   };
@@ -749,7 +751,6 @@ class locale_tgt_t {
 %type   <refer>         alloc_ret
 
 %type  <field>         log_term rel_expr rel_abbr eval_abbr
-%type   <refer>                num_value num_term value factor
 %type   <refer>         simple_cond bool_expr until_expr
 %type  <log_expr_t>    log_expr rel_abbrs eval_abbrs
 %type   <rel_term_t>   rel_term rel_term1
@@ -793,9 +794,13 @@ class locale_tgt_t {
 %type   <refer>         alphaval alpha_val numeref scalar scalar88 scalar_any
 %type   <refer>         tableref tableish
 %type   <refer>         varg varg1 varg1a start_after start_pos
-%type   <refer>         expr expr_term compute_expr free_tgt by_value_arg
+%type   <refer>         cexpr free_tgt by_value_arg
 %type   <refer>         move_tgt read_key read_into vary_by
-%type   <refer>         num_operand envar search_expr any_arg
+%type   <refer>         num_operand num_value envar search_expr any_arg
+
+%type   <ast_op>        expr expr_term num_term value factor
+                        compute_expr
+
 %type   <accept_func>  accept_body
 %type   <refers>        subscript_exprs subscripts arg_list free_tgts 
 %type   <targets>       move_tgts set_tgts
@@ -972,7 +977,7 @@ class locale_tgt_t {
     struct { bool tf; cbl_field_t *field; } bool_field;
     struct { int token; cbl_field_t *cond; } cond_field;
     struct cbl_refer_t *refer;
-
+           ast_op_t *ast_op;
     struct rel_term_type { bool invert; cbl_refer_t *term; } rel_term_t;
     struct log_expr_t *log_expr_t;
     struct vargs_t* vargs;
@@ -992,8 +997,7 @@ class locale_tgt_t {
            linage_t linage;
            linage_value_t linage_value;
     struct arith_t *arith;
-    struct { size_t ntgt; cbl_num_result_t *tgts;
-             cbl_refer_t *expr; } compute_body_t;
+    struct { size_t ntgt; cbl_num_result_t *tgts; ast_op_t *ast_op; } 
compute_body_t;
     struct cbl_inspect_t *insp_one;
            cbl_inspect_opers_t *insp_all;
     struct cbl_inspect_oper_t *insp_oper;
@@ -1079,6 +1083,8 @@ class locale_tgt_t {
                        $$.term? name_of($$.term->field) : "<none>"); } 
<rel_term_t>
 %printer { fprintf(yyo, "%s", $$->dbgstr()); } <log_expr_t>
 
+%printer { fprintf(yyo, "%lu args\n", (unsigned long)$$->args.size()); 
$$->dump(); } <vargs>
+
 %printer { fprintf(yyo, "%s (token %d)", keyword_str($$), $$ ); } relop
 %printer { fprintf(yyo, "'%s'", $$? $$ : "" ); } NAME <string>
 %printer { fprintf(yyo, "%s'%.*s'{" HOST_SIZE_T_PRINT_UNSIGNED "} %s",
@@ -1566,7 +1572,7 @@ class locale_tgt_t {
 %locations
 %token-table
 %define parse.error verbose // custom
-%expect 7
+%expect 6
 %require "3.5.1"  //    3.8.2 also works, but not 3.8.0
 %%
 
@@ -5935,12 +5941,12 @@ accept_body:    ACCEPT scalar[r]
                  $$.func = accept_done_e;
                   parser_accept_command_line(*$r, NULL, NULL, NULL );
                 }
-        |       ACCEPT scalar[r] FROM COMMAND_LINE '(' expr ')'
+        |       ACCEPT scalar[r] FROM COMMAND_LINE '(' cexpr ')'
                 {
                   statement_begin(@1, ACCEPT);
                  $$.func = accept_command_line_e;
                  $$.into = $r;
-                 $$.from = $expr;
+                 $$.from = $cexpr;
                 }
         |       ACCEPT scalar[r] FROM COMMAND_LINE_COUNT
                 {
@@ -6232,7 +6238,7 @@ scalar88: name88 subscripts[subs] refmod[ref]
                 }
                 ;
 
-allocate:       ALLOCATE expr[size] CHARACTERS initialized RETURNING 
scalar[returning]
+allocate:       ALLOCATE cexpr[size] CHARACTERS initialized RETURNING 
scalar[returning]
                 {
                   statement_begin(@1, ALLOCATE);
                   if( $size->field->type == FldLiteralN ) {
@@ -6270,22 +6276,33 @@ alloc_ret:      %empty { static cbl_refer_t empty; $$ = 
&empty; }
         |       RETURNING scalar[name]           { $$ = $name; }
                 ;
 
-compute:        compute_impl end_compute { current.compute_end(); }
-        |       compute_cond end_compute { current.compute_end(); }
+compute:        compute_impl end_compute
+        |       compute_cond end_compute
                 ;
 compute_impl:   COMPUTE compute_body[body]
                 {
-                  parser_assign( $body.ntgt, $body.tgts, *$body.expr,
-                                 NULL, NULL, current.compute_label() );
+                  std::vector<cbl_num_result_t> results($body.tgts,
+                                                        $body.tgts + 
$body.ntgt);
+                  $body.ast_op->show(results); // always a good idea to show 
results
+                  $body.ast_op->rpn_sanity_check();
+                  parser_compute( results, $body.ast_op->as_deque(), 
+                                  nullptr, nullptr, 
+                                  current.compute_label() );
+                  $body.ast_op->reset();
                   current.declaratives_evaluate();
+                  current.compute_end();
                 }
                 ;
 compute_cond:   COMPUTE compute_body[body] arith_errs[err]
                 {
-                  parser_assign( $body.ntgt, $body.tgts, *$body.expr,
-                                 $err.on_error, $err.not_error,
-                                 current.compute_label() );
+                  std::vector<cbl_num_result_t> results($body.tgts,
+                                                        $body.tgts + 
$body.ntgt);
+                  parser_compute( results, $body.ast_op->as_deque(), 
+                                  $err.on_error, $err.not_error,
+                                  current.compute_label() );
+                  $body.ast_op->reset();
                   current.declaratives_evaluate();
+                  current.compute_end();
                 }
                 ;
 end_compute:    %empty %prec COMPUTE
@@ -6296,16 +6313,15 @@ compute_body:   rnames { statement_begin(@$, COMPUTE); 
} compute_expr[expr] {
                   $$.ntgt = rhs.size();
                   auto C = new cbl_num_result_t[$$.ntgt];
                   $$.tgts = use_any(rhs, C);
-                  $$.expr = $expr;
+                  $$.ast_op = $expr;
                 }
                 ;
-compute_expr:   EQ {
+compute_expr:   EQ expr {
                   if( $1[0] == 'E' ) { // lexer found EQUALS keyword
                     dialect_ok(@1, IbmEqualAssignE,
                                "EQUAL as assignment operator" );
                   }
                   current.compute_begin();
-                } expr {
                   $$ = $expr;
                 }
                 ;
@@ -6571,9 +6587,9 @@ continue_stmt:  CONTINUE {
                   statement_begin(@1, CONTINUE);
                   parser_sleep(*cbl_refer_t::empty());
                 }
-        |      CONTINUE AFTER expr SECONDS {
+        |      CONTINUE AFTER cexpr SECONDS {
                   statement_begin(@1, CONTINUE);
-                  parser_sleep(*$expr);
+                  parser_sleep(*$cexpr);
                 }
                 ;
 
@@ -6700,42 +6716,42 @@ simple_cond:    kind_of_name
                                bit_on_op : bit_off_op;
                    parser_bitop($$->cond(), parent, op, value );
                 }
-        |       expr is CLASS_NAME[domain]
+        |       cexpr is CLASS_NAME[domain]
                 {
                   $$ = new_reference(new_temporary(FldConditional));
                   // symbol_find does not find FldClass symbols
                   struct symbol_elem_t *e = symbol_field(PROGRAM, 0, $domain);
                   parser_setop($$->cond(), $1->field, is_op, cbl_field_of(e));
                 }
-        |       expr NOT CLASS_NAME[domain] {
+        |       cexpr NOT CLASS_NAME[domain] {
                   $$ = new_reference(new_temporary(FldConditional));
                   // symbol_find does not find FldClass symbols
                   struct symbol_elem_t *e = symbol_field(PROGRAM, 0, $domain);
                   parser_setop($$->cond(), $1->field, is_op, cbl_field_of(e));
                   parser_logop($$->cond(), NULL, not_op, $$->cond());
                 }
-        |       expr is OMITTED
+        |       cexpr is OMITTED
                 {
-                  auto lhs = cbl_refer_t($expr->field);
+                  auto lhs = cbl_refer_t($cexpr->field);
                   lhs.addr_of = true;
                   auto rhs = cbl_field_of(symbol_field(0,0, "NULLS"));
                   $$ = new_reference(new_temporary(FldConditional));
                   ast_relop(@$, $$->field, lhs, eq_op, rhs);
                 }
-        |       expr /* IS */ NOT OMITTED
+        |       cexpr /* IS */ NOT OMITTED
                { // IS captured by lexer
-                  auto lhs = cbl_refer_t($expr->field);
+                  auto lhs = cbl_refer_t($cexpr->field);
                   lhs.addr_of = true;
                   auto rhs = cbl_field_of(symbol_field(0,0, "NULLS"));
                   $$ = new_reference(new_temporary(FldConditional));
                   ast_relop(@$, $$->field, lhs, ne_op, rhs);
                 }
-        |       expr /* IS */ posneg[op] {
+        |       cexpr /* IS */ posneg[op] {
                   $$ = new_reference(new_temporary(FldConditional));
                   relop_t op = static_cast<relop_t>($op);
                   cbl_field_t *zero = constant_of(constant_index(ZERO));
                   if( $1->field->type == FldPointer ) {
-                    error_msg(@expr, "cannot compare %qs (%s) to zero",
+                    error_msg(@cexpr, "cannot compare %qs (%s) to zero",
                               nice_name_of($1->field),
                               cbl_field_type_name($1->field->type));
                     YYERROR;
@@ -6756,7 +6772,7 @@ simple_cond:    kind_of_name
                 }
                 ;
 
-kind_of_name:   expr might_be variable_type
+kind_of_name:   cexpr might_be variable_type
                 {
                   $$ = new_temporary(FldConditional);
                   enum classify_t type = classify_of($3);
@@ -6953,7 +6969,7 @@ rel_term1:        all LITERAL
                   $$.term = new_reference(constant_of(constant_index(ZERO)));
                   $$.term->all = true;
                 }
-        |       expr {
+        |       cexpr {
                  $$.invert = false;
                  $$.term = $1;
                }
@@ -6963,41 +6979,54 @@ rel_term1:      all LITERAL
                }
                 ;
 
+cexpr:          expr {
+                  $$ = $expr->compute($expr);
+                }
+                ;
 expr:           expr_term
                 ;
 expr_term:      expr_term '+' num_term
                 {
-                  if( ($$ = ast_op(@$, $1, '+', $3)) == NULL  ) YYERROR;
+                  if( ! ast_op_t::op_ok(@$, '+', $3) ) YYERROR;
+                  $$ = &$1->push_op('+', *$3);
                 }
         |       expr_term '-' num_term
                 {
-                  if( ($$ = ast_op(@$, $1, '-', $3)) == NULL  ) YYERROR;
+                  if( ! ast_op_t::op_ok(@$, '-', $3) ) YYERROR;
+                  $$ = &$1->push_op('-', *$3);
                 }
         |       num_term
                 ;
 
 num_term:       num_term '*' value
                 {
-                  if( ($$ = ast_op(@$, $1, '*', $3)) == NULL  ) YYERROR;
+                  if( ! ast_op_t::op_ok(@$, '*', $3) ) YYERROR;
+                  $$ = &$1->push_op('*', *$3);
                 }
         |       num_term '/' value
                 {
-                  if( ($$ = ast_op(@$, $1, '/', $3)) == NULL  ) YYERROR;
+                  if( ! ast_op_t::op_ok(@$, '/', $3) ) YYERROR;
+                  $$ = &$1->push_op('/', *$3);
                 }
         |       value
         ;
 
 value:          value POW factor
                 {
-                  if( ($$ = ast_op(@$, $1, '^', $3)) == NULL  ) YYERROR;
+                  if( ! ast_op_t::op_ok(@$, '^', $3) ) YYERROR;
+                  $$ = &$1->push_op('^', *$3);
                 }
-        |       '-' value       %prec NEG { $$ = negate( $2 );}
-        |       '+' factor %prec NEG { $$ = $2;}
-        |       factor[rhs]
+        |       '-' value[operand]  %prec NEG { $$ = &$operand->push_op('!'); }
+        |       '+' factor          %prec NEG { $$ = $2;}
+        |       factor
                 ;
 
 factor:         '(' expr ')' { $$ = $2; }
-        |       num_value { $$ = $num_value; }
+        |       num_value
+                {
+                  $$ = new ast_op_t;
+                  $$->expr($num_value);
+                }
                 ;
 
 if_stmt:        if_impl end_if
@@ -7066,7 +7095,7 @@ eval_subject:   eval_subject1 {
                }
                 ;
 eval_subject1:  bool_expr
-       |       expr
+       |       cexpr
         |       true_false
                 {
                   static cbl_field_t *zero = constant_of(constant_index(ZERO));
@@ -7391,14 +7420,14 @@ tableish:       name subscripts[subs] refmod[ref]  
%prec NAME
                }
                 ;
 
-refmod:         LPAREN expr[from] ':' expr[len] ')' %prec NAME
+refmod:         LPAREN cexpr[from] ':' cexpr[len] ')' %prec NAME
                 {
                  if( ! require_integer(@from, *$from) ) YYERROR;
                  if( ! require_integer(@len, *$len) ) YYERROR;
                   $$.from = $from;
                   $$.len = $len;
                 }
-        |       LPAREN expr[from] ':'           ')' %prec NAME
+        |       LPAREN cexpr[from] ':'           ')' %prec NAME
                 {
                  if( ! require_integer(@from, *$from) ) YYERROR;
                   $$.from = $from;
@@ -8105,19 +8134,19 @@ subscripts:     LPAREN subscript_exprs ')' {
                  }
                }
                 ;
-subscript_exprs:       expr
+subscript_exprs:       cexpr
                {
-                 if( ! require_integer(@expr, *$expr) ) YYERROR;
-                 $$ = new refer_list_t($expr);
+                 if( ! require_integer(@cexpr, *$cexpr) ) YYERROR;
+                 $$ = new refer_list_t(*$cexpr);
                }
-        |       subscript_exprs expr {
+        |       subscript_exprs cexpr {
                   if( $1->size() == MAXIMUM_TABLE_DIMENSIONS ) {
                     error_msg(@1, "table dimensions limited to %d",
                              MAXIMUM_TABLE_DIMENSIONS);
                     YYERROR;
                   }
-                 if( ! require_integer(@expr, *$expr) ) YYERROR;
-                  $1->push_back($2); $$ = $1;
+                 if( ! require_integer(@cexpr, *$cexpr) ) YYERROR;
+                  $1->push_back(*$2); $$ = $1;
                 }
         |       ALL {
                   auto ref = new_reference(constant_of(constant_index(ZERO)));
@@ -8125,10 +8154,10 @@ subscript_exprs:        expr
                 }
                 ;
 
-arg_list:                any_arg { $$ = new refer_list_t($1); }
-        |       arg_list any_arg { $1->push_back($2); $$ = $1; }
+arg_list:                any_arg { $$ = new refer_list_t(*$1); }
+        |       arg_list any_arg { $1->push_back(*$2); $$ = $1; }
                 ;
-any_arg:        expr
+any_arg:        cexpr
         |       LITERAL {$$ = new_reference(new_literal(@1, $1, quoted_e)); }
                 ;
 
@@ -9210,8 +9239,8 @@ delete_file_body:
                 }
                 ;
 retry_phrase:   %empty
-        |       RETRY expr TIMES
-        |       FOR expr  SECONDS
+        |       RETRY cexpr TIMES
+        |       FOR cexpr  SECONDS
         |       FOREVER {
                   cbl_unimplemented("DELETE FILE RETRY");
                 }
@@ -9391,12 +9420,12 @@ start_body:     filename[file]
                   $$ = file_start_args.init(@file, $file);
                   parser_file_start( $file, relop_of($relop), key, ksize );
                 }
-        |       filename[file] KEY relop name[key] with LENGTH expr
+        |       filename[file] KEY relop name[key] with LENGTH cexpr
                 { // lexer swallows IS, although relop allows it.
                   statement_begin(@$, START);
                   int key = $file->key_one($key);
                   $$ = file_start_args.init(@file, $file);
-                  parser_file_start( $file, relop_of($relop), key, *$expr );
+                  parser_file_start( $file, relop_of($relop), key, *$cexpr );
                 }
         |       filename[file] FIRST
                 {
@@ -9845,7 +9874,7 @@ search_term:    scalar[key] EQ search_expr[sarg]
                                        is_ascending_key(key) );
                 }
                 ;
-search_expr:    expr
+search_expr:    cexpr
         |       LITERAL { $$ = new_reference(new_literal(@1, $1, quoted_e)); }
                 ;
 
@@ -10698,7 +10727,7 @@ ffi_by_ref:     scalar_arg[refer]
                 }
                 ;
 
-ffi_by_con:     expr
+ffi_by_con:     cexpr
                 {
                   cbl_refer_t *r = new cbl_refer_t(*$1);
                   $$ = new cbl_ffi_arg_t(by_content_e, r);
@@ -10929,7 +10958,6 @@ label_1:        qname
 
   /* string & unstring */
 
-
 string:         string_impl end_string
         |       string_cond end_string
                 ;
@@ -11332,12 +11360,12 @@ intrinsic:      function_udf
                  cbl_unimplemented("BASECONVERT");
                   if( ! intrinsic_call_3($$, BASECONVERT, $r1, $r2, $r3 )) 
YYERROR;
                 }
-        |       BIT_OF  '(' expr[r1] ')' {
+        |       BIT_OF  '(' cexpr[r1] ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("BIT-OF");
                   if( ! intrinsic_call_1($$, BIT_OF, $r1, @r1)) YYERROR;
                 }
-        |       CHAR  '(' expr[r1] ')' {
+        |       CHAR  '(' cexpr[r1] ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("CHAR");
                   if( ! intrinsic_call_1($$, CHAR, $r1, @r1)) YYERROR;
@@ -11421,7 +11449,7 @@ intrinsic:      function_udf
                   parser_intrinsic_find_string($$, *$r1, *$r2, $after, $last, 
$anycase);
                 }
 
-        |       FORMATTED_DATE '(' DATE_FMT[r1] expr[r2] ')' {
+        |       FORMATTED_DATE '(' DATE_FMT[r1] cexpr[r2] ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("FORMATTED-DATE");
                   auto r1 = new_reference(new_literal(strlen($r1), $r1, 
quoted_e));
@@ -11430,8 +11458,8 @@ intrinsic:      function_udf
                 }
 
 
-        |       FORMATTED_DATETIME '(' DATETIME_FMT[r1] expr[r2]
-                                                        expr[r3] ')' {
+        |       FORMATTED_DATETIME '(' DATETIME_FMT[r1] cexpr[r2]
+                                                        cexpr[r3] ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("FORMATTED-DATETIME");
                   auto r1 = new_reference(new_literal(strlen($r1), $r1, 
quoted_e));
@@ -11440,8 +11468,8 @@ intrinsic:      function_udf
                   if( ! intrinsic_call_4($$, FORMATTED_DATETIME,
                                          r1, $r2, $r3, &r3) ) YYERROR;
                 }
-        |       FORMATTED_DATETIME '(' DATETIME_FMT[r1] expr[r2]
-                                        expr[r3] expr[r4] ')' {
+        |       FORMATTED_DATETIME '(' DATETIME_FMT[r1] cexpr[r2]
+                                        cexpr[r3] cexpr[r4] ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("FORMATTED-DATETIME");
                   auto r1 = new_reference(new_literal(strlen($r1), $r1, 
quoted_e));
@@ -11452,8 +11480,8 @@ intrinsic:      function_udf
         |       FORMATTED_DATETIME '(' error ')' {
                   YYERROR;
                 }
-        |       FORMATTED_TIME '(' TIME_FMT[r1] expr[r2]
-                                                expr[r3]  ')' {
+        |       FORMATTED_TIME '(' TIME_FMT[r1] cexpr[r2]
+                                                cexpr[r3]  ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("FORMATTED-DATETIME");
                   auto r1 = new_reference(new_literal(strlen($r1), $r1, 
quoted_e));
@@ -11461,7 +11489,7 @@ intrinsic:      function_udf
                   if( ! intrinsic_call_3($$, FORMATTED_TIME,
                                              r1, $r2, $r3) ) YYERROR;
                 }
-        |       FORMATTED_TIME '(' TIME_FMT[r1] expr[r2]  ')' {
+        |       FORMATTED_TIME '(' TIME_FMT[r1] cexpr[r2]  ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("FORMATTED-TIME");
                   auto r1 = new_reference(new_literal(strlen($r1), $r1, 
quoted_e));
@@ -11585,7 +11613,7 @@ intrinsic:      function_udf
                   $$ = new_tempnumeric_float("RANDOM");
                   parser_intrinsic_call_0( $$, intrinsic_cname(RANDOM) );
                 }
-        |       RANDOM_SEED expr[r1] ')'
+        |       RANDOM_SEED cexpr[r1] ')'
                 { // left parenthesis consumed by lexer
                   location_set(@1);
                   $$ = new_tempnumeric_float("RANDOM-SEED");
@@ -11644,7 +11672,7 @@ intrinsic:      function_udf
                  * TRIM (arg-1 arg-2a arg-2b) is the same as 
                  * TRIM (TRIM (arg-1 arg-2a) arg-2b).
                  */
-        |       TRIM '(' expr[r1] trim_trailing[how] trim_expr[args2] ')'
+        |       TRIM '(' cexpr[r1] trim_trailing[how] trim_expr[args2] ')'
                 {
                   location_set(@1);
                    switch( $r1->field->type ) {
@@ -11671,21 +11699,21 @@ intrinsic:      function_udf
                   parser_trim($$, *$r1, $how, args);
                 }  
 
-        |       USUBSTR '(' alpha_val[r1] expr[r2] expr[r3]  ')' {
+        |       USUBSTR '(' alpha_val[r1] cexpr[r2] cexpr[r3]  ')' {
                   location_set(@1);
                   $$ = new_alphanumeric("USUBSTR");
                   if( ! intrinsic_call_3($$, FORMATTED_DATETIME,
                                              $r1, $r2, $r3) ) YYERROR;
                 }
 
-        |       intrinsic_I  '(' expr[r1] ')'
+        |       intrinsic_I  '(' cexpr[r1] ')'
                 {
                   location_set(@1);
                   $$ = new_tempnumeric(keyword_str($1));
                   if( ! intrinsic_call_1($$, $1, $r1, @r1)) YYERROR;
                 }
 
-        |       intrinsic_N  '(' expr[r1] ')'
+        |       intrinsic_N  '(' cexpr[r1] ')'
                 {
                   location_set(@1);
                   $$ = new_tempnumeric_float(keyword_str($1));
@@ -11717,14 +11745,14 @@ intrinsic:      function_udf
                   if( ! intrinsic_call_1($$, $1, $r1, @r1)) YYERROR;
                 }
 
-        |       intrinsic_I2 '(' expr[r1] expr[r2] ')'
+        |       intrinsic_I2 '(' cexpr[r1] cexpr[r2] ')'
                 {
                   location_set(@1);
                   $$ = new_tempnumeric("intrinsic_I2");
                   if( ! intrinsic_call_2($$, $1, $r1, $r2) ) YYERROR;
                 }
 
-        |       DATE_TO_YYYYMMDD '(' expr[r1] ')'
+        |       DATE_TO_YYYYMMDD '(' cexpr[r1] ')'
                 {
                   location_set(@1);
                   static auto r2 = new_reference(FldNumericDisplay, "50");
@@ -11741,7 +11769,7 @@ intrinsic:      function_udf
                                          $r1, r2, r3) ) YYERROR;
                 }
 
-        |       DATE_TO_YYYYMMDD '(' expr[r1] expr[r2] ')'
+        |       DATE_TO_YYYYMMDD '(' cexpr[r1] cexpr[r2] ')'
                 {
                   location_set(@1);
                   static auto one = new cbl_refer_t( new_constant("1") );
@@ -11757,8 +11785,8 @@ intrinsic:      function_udf
                                          $r1, $r2, r3) ) YYERROR;
                 }
 
-        |       DATE_TO_YYYYMMDD '(' expr[r1]
-                                     expr[r2] expr[r3] ')'
+        |       DATE_TO_YYYYMMDD '(' cexpr[r1]
+                                     cexpr[r2] cexpr[r3] ')'
                 {
                   location_set(@1);
                   $$ = new_tempnumeric("DATE_TO_YYYYMMDD");
@@ -11766,7 +11794,7 @@ intrinsic:      function_udf
                                          $r1, $r2, $r3) ) YYERROR;
                 }
 
-        |       DAY_TO_YYYYDDD '(' expr[r1] ')'
+        |       DAY_TO_YYYYDDD '(' cexpr[r1] ')'
                 {
                   location_set(@1);
                   static auto r2 = new_reference(FldNumericDisplay, "50");
@@ -11783,7 +11811,7 @@ intrinsic:      function_udf
                                          $r1, r2, r3) ) YYERROR;
                 }
 
-        |       DAY_TO_YYYYDDD '(' expr[r1] expr[r2] ')'
+        |       DAY_TO_YYYYDDD '(' cexpr[r1] cexpr[r2] ')'
                 {
                   location_set(@1);
                   static auto one = new cbl_refer_t( new_constant("1") );
@@ -11799,8 +11827,8 @@ intrinsic:      function_udf
                                          $r1, $r2, r3) ) YYERROR;
                 }
 
-        |       DAY_TO_YYYYDDD '(' expr[r1]
-                                     expr[r2] expr[r3] ')'
+        |       DAY_TO_YYYYDDD '(' cexpr[r1]
+                                     cexpr[r2] cexpr[r3] ')'
                 {
                   location_set(@1);
                   $$ = new_tempnumeric("DAY_TO_YYYYDDD");
@@ -11808,7 +11836,7 @@ intrinsic:      function_udf
                                          $r1, $r2, $r3) ) YYERROR;
                 }
 
-        |       YEAR_TO_YYYY '(' expr[r1] ')'
+        |       YEAR_TO_YYYY '(' cexpr[r1] ')'
                 {
                   location_set(@1);
                   static auto r2 = new_reference(new_constant("50"));
@@ -11825,7 +11853,7 @@ intrinsic:      function_udf
                                          $r1, r2, r3) ) YYERROR;
                 }
 
-        |       YEAR_TO_YYYY '(' expr[r1] expr[r2] ')'
+        |       YEAR_TO_YYYY '(' cexpr[r1] cexpr[r2] ')'
                 {
                   location_set(@1);
                   static auto one = new cbl_refer_t( new_constant("1") );
@@ -11841,8 +11869,8 @@ intrinsic:      function_udf
                                          $r1, $r2, r3) ) YYERROR;
                 }
 
-        |       YEAR_TO_YYYY '(' expr[r1]
-                                     expr[r2] expr[r3] ')'
+        |       YEAR_TO_YYYY '(' cexpr[r1]
+                                     cexpr[r2] cexpr[r3] ')'
                 {
                   location_set(@1);
                   $$ = new_tempnumeric("YEAR_TO_YYYY");
@@ -11850,7 +11878,7 @@ intrinsic:      function_udf
                                          $r1, $r2, $r3) ) YYERROR;
                 }
 
-        |       intrinsic_N2 '(' expr[r1] expr[r2] ')'
+        |       intrinsic_N2 '(' cexpr[r1] cexpr[r2] ')'
                 {
                   location_set(@1);
                   switch($1) {
@@ -13656,32 +13684,60 @@ cbl_key_t::operator=( const sort_key_t& that ) {
   return *this;
 }
 
-static cbl_refer_t *
-ast_op( const cbl_loc_t& loc, cbl_refer_t *lhs, char op, cbl_refer_t *rhs ) {
-  assert(lhs);
-  assert(rhs);
-  if( ! (is_numeric(lhs->field) && is_numeric(rhs->field)) ) {
-    // If one of the fields isn't numeric, allow for index addition.
+ast_op_t::choose_intermediate_type& 
+ast_op_t::choose_intermediate_type::select_highest( const rpn_t& rpn ) {
+  const cbl_field_t *field = rpn.term.field;
+
+  if( field ) {  
+    if( ! is_numeric(field) ) { output = *field; return *this; }
+    if( output.type == FldFloat ) return *this;
+
+    if( field->type == FldFloat ) {
+      output.type = FldFloat;
+      output.data.capacity(16);
+      output.attr = intermediate_e;
+    }
+    this->operand = field;
+    return *this;
+  }
+  
+  if( rpn.op == '*' ) {
+    if( operand ) {
+      output.data.digits += operand->data.digits;
+    }
+    if( output.data.digits > MAX_FIXED_POINT_DIGITS) {
+      output.type = FldFloat;
+      output.data.capacity(16);
+      output.attr = intermediate_e;
+    }
+  }
+  return *this;
+}
+
+bool
+ast_op_t::op_ok( const cbl_loc_t& loc, char op, const ast_op_t *rhstack )
+{
+  assert(rhstack);
+  
+  const rpn_t& rpn(rhstack->top());
+  const cbl_refer_t& rhs(rpn.term);
+  gcc_assert( rhs.field || rpn.op );
+
+  if( rhs.field && ! is_numeric(rhs.field) ) {
+    // If the field isn't numeric, allow for index addition.
     switch(op) {
     case '+':
     case '-':
       // Simple addition OK for table indexes.
-      if( lhs->field->type == FldIndex || rhs->field->type == FldIndex ) {
-        goto ok;
+      if( rhs.field->type == FldIndex ) {
+        return true;
       }
     }
 
-    auto f  = !is_numeric(lhs->field)? lhs->field : rhs->field;
-    error_msg(loc, "%qs is not numeric", f->name);
-    return NULL;
-  }
- ok:
-  cbl_field_t skel = determine_intermediate_type( *lhs, op, *rhs );
-  cbl_refer_t *tgt = new_reference_like(skel);
-  if( !mode_syntax_only() ) {
-    parser_op( *tgt, *lhs, op, *rhs, current.compute_label() );
+    error_msg(loc, "%qs is not numeric", rhs.field->name);
+    return false;
   }
-  return tgt;
+  return true;
 }
 
 /*
diff --git a/gcc/cobol/parse_ante.h b/gcc/cobol/parse_ante.h
index 49eb90b265ab..c2cb3437d0b7 100644
--- a/gcc/cobol/parse_ante.h
+++ b/gcc/cobol/parse_ante.h
@@ -631,11 +631,156 @@ struct arith_t {
   }
 };
 
-static cbl_refer_t * ast_op( const cbl_loc_t& loc,
-                             cbl_refer_t *lhs, char op, cbl_refer_t *rhs );
+static void 
+ast_relop( const cbl_loc_t& loc, cbl_field_t *tgt,
+           cbl_refer_t lhs, relop_t op, cbl_refer_t rhs );
 
-static void ast_relop( const cbl_loc_t& loc, cbl_field_t *tgt,
-                       cbl_refer_t lhs, relop_t relop, cbl_refer_t rhs );
+
+/*
+ * Collect an RPN stack of operations.  The compute() member function calls
+ * parser_compute to processes the stack to a target.  Alternatively, the
+ * COMPUTE statement calls parser_compute with a list of one or more targets.
+ */
+struct ast_op_t : private std::stack<rpn_t>{  
+  cbl_label_t *lbl; // the COMPUTE error label
+ public:
+  ast_op_t() : lbl(nullptr) {}
+
+  cbl_refer_t * operator=( cbl_refer_t * term ) {
+    top() = rpn_t(*term);
+    return term;
+  }
+
+  static bool op_ok( const cbl_loc_t& loc, char op, const ast_op_t *rhs );
+
+  cbl_refer_t * expr( cbl_refer_t * term ) {
+    dbgmsg("ast_op_t::%s:%d: %s", __func__, __LINE__, field_str(term->field));
+    push( rpn_t(*term) );
+    return term;
+  }
+
+  ast_op_t&  push_op( char op, const ast_op_t& rhs = ast_op_t() ) {
+    c.insert( c.end(), rhs.c.begin(), rhs.c.end() );
+    push( rpn_t(op) );
+    rpn_dump(c);
+    return *this;
+  }
+
+  cbl_refer_t * compute( cbl_refer_t *tgt ) {
+    gcc_assert( ! empty() );
+    if( 1 < c.size() ) {
+      tgt = compute();
+    }
+    return tgt;
+  }
+  
+  cbl_refer_t * compute( ast_op_t *operand ) {
+    return c.size() == 1 ? &operand->top().term : compute();
+  }
+  
+  /*
+   * choose_intermediate_type is a functor that defaults to FldNumericBin5.  If
+   * while iterating over the operands it determines that one is FldFloat, or
+   * that the required digits exceeds the maximum, it selects FldFloat instead.
+   */
+  class choose_intermediate_type {
+    cbl_field_t output;
+    const cbl_field_t *operand;
+   public:
+    choose_intermediate_type() : output( FldNumericBin5,
+                                         (intermediate_e | signable_e),
+                                         {}, 0, "", {} )
+                               , operand(nullptr)
+    {
+      output.data.capacity(16);
+      output.data.digits   = MAX_FIXED_POINT_DIGITS;
+    }
+    choose_intermediate_type& select_highest( const rpn_t& rpn );
+    choose_intermediate_type& operator()( const rpn_t& rpn ) {
+      return select_highest(rpn);
+    }
+    cbl_field_t field() const { return output; }
+  };
+
+  cbl_field_t intermediate_type() const {
+    const auto& selected = std::for_each( c.rbegin(), c.rend(),
+                                          choose_intermediate_type() );
+    return selected.field();
+  }
+
+  const std::deque<rpn_t>& as_deque() const { return this->c; }
+  void reset() { c.clear(); }
+
+  void show( std::vector<cbl_num_result_t>& results ) {
+    if( yydebug ) {
+      int i=0;
+      for( const auto& result : results ) {
+        dbgmsg( "result %u: %s", i++, result.refer.str() );
+      }
+      rpn_dump(c);
+    }
+  }
+                
+  bool rpn_sanity_check() {
+    auto n = std::accumulate( c.rbegin(), c.rend(), 0,
+                              []( int n, const rpn_t& rpn ) {
+                                if( rpn.term.field ) return ++n;
+                                switch(rpn.op) {
+                                case '+': case '-':
+                                case '*': case '/': case '^': return --n;
+                                case '!': return n; // unuary minus
+                                }
+                                dbgmsg("rpn_sanity_check: n=%d, neither field 
nor op", n);
+                                gcc_unreachable();
+                              } );
+    if( n != 1 ) rpn_dump(c);
+    dbgmsg("rpn_sanity_check: n=%d, %s", n, n == 1? "ok" : "bzzt");
+    return n == 1;
+  }
+
+ protected:
+  bool valid_size() const {
+    return 2 < c.size() || top().op == '!';
+  }
+  cbl_refer_t * compute() {
+    gcc_assert( ! empty() );
+    gcc_assert( 1 < c.size() );
+
+    const cbl_field_t& skel = intermediate_type();
+    cbl_refer_t *tgt = new_reference_like(skel);
+    dbgmsg("ast_op_t::%s:%d: target %s capacity %u", __func__, __LINE__,
+           cbl_field_type_str(tgt->field->type), tgt->field->data.capacity());
+    if( !valid_size() ) {
+      yydebug = 1;
+      rpn_dump(c);
+    }
+    // We have at least 3 operands, or the first operator is unary negation.
+    gcc_assert( valid_size() );
+    rpn_dump(c); // for now
+    rpn_sanity_check();
+    
+    parser_compute(tgt, c, lbl);
+    
+    this->c.clear();
+    dbgmsg("ast_op_t::%s:%d: output %s %s capacity %u", __func__, __LINE__,
+           cbl_field_type_str(tgt->field->type), nice_name_of(tgt->field), 
+           tgt->field->data.capacity());
+    return tgt;
+  }
+
+  static void rpn_dump( const std::deque<rpn_t>& c ) {
+    dbgmsg("ast_op_t::%s:%d: %lu members", __func__, __LINE__, (unsigned 
long)c.size());
+    for( const auto& operand : c ) {
+      auto f = operand.term.field;
+      if( f ) {
+        auto type = cbl_field_type_str(f->type);
+        dbgmsg("ast_op_t::%s:%d: %-20s %s", __func__, __LINE__, type, 
field_str(f));
+      } else {
+        dbgmsg("ast_op_t::%s:%d: %c", __func__, __LINE__, operand.op);
+      }
+    }
+  }
+};
 
 static void ast_add( arith_t *arith );
 static bool ast_subtract( arith_t *arith );
@@ -1086,11 +1231,19 @@ struct refer_list_t {
       delete refer;
     }
   }
+  // the source is not always to be deleted
+  explicit refer_list_t( const cbl_refer_t& refer ) {
+    refers.push_back(refer);
+  }
   refer_list_t * push_back( cbl_refer_t *refer ) {
     refers.push_back(*refer);
     delete refer;
     return this;
   }
+  refer_list_t * push_back( const cbl_refer_t& refer ) {
+    refers.push_back(refer);
+    return this;
+  }
   inline list<cbl_refer_t>& items() { return  refers; }
   inline list<cbl_refer_t>::iterator begin() { return  refers.begin(); }
   inline list<cbl_refer_t>::iterator end()   { return  refers.end(); }
@@ -1378,9 +1531,15 @@ static  list<cbl_refer_t> lhs;
 
 struct vargs_t {
   std::list<cbl_refer_t> args;
-    vargs_t() {}
-    explicit vargs_t( struct cbl_refer_t *p ) { args.push_back(*p); delete p; }
-    void push_back( cbl_refer_t *p ) { args.push_back(*p); delete p; }
+  vargs_t() {}
+  explicit vargs_t( struct cbl_refer_t *p ) { args.push_back(*p); delete p; }
+  void push_back( cbl_refer_t *p ) { args.push_back(*p); delete p; }
+  void dump() const {
+    int i=0;
+    for( auto arg : args ) {
+      dbgmsg("\t%3d: %s", i++, arg.str());
+    }
+  }
 };
 
 static const char intermediate[] = ":intermediate";
diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h
index ddbe3176df75..6ab34a5df877 100644
--- a/gcc/cobol/symbols.h
+++ b/gcc/cobol/symbols.h
@@ -1109,6 +1109,34 @@ struct cbl_refer_t {
   }
 };
 
+/*
+ * An element in the RPN stack for expression evaluation, either an operation
+ * or an operand.  A NUL operator indicates an operand. 
+ */
+struct rpn_t {
+  char op;
+  cbl_refer_t term;
+  rpn_t( char op ) : op(op) { // cppcheck-suppress noExplicitConstructor
+    static const char ops[] = "+-*/^!";
+    gcc_assert( std::any_of(ops, ops + sizeof(ops),
+                            [op]( char ch ) { return op == ch; }) );
+  }
+  rpn_t( const cbl_refer_t &term ) // cppcheck-suppress noExplicitConstructor
+    : op('\0'), term(term)
+  {}
+};
+
+struct expr_t {
+  char op;
+  cbl_refer_t lhs, rhs;
+  cbl_label_t *lbl;
+  expr_t( char op,
+          const cbl_refer_t& lhs, const cbl_refer_t& rhs,
+          cbl_label_t *lbl )
+    : op(op), lhs(lhs), rhs(rhs), lbl(lbl)
+  {}
+};
+
 struct elem_key_t {
   size_t program;
   const char * name;
diff --git a/libgcobol/gmath.cc b/libgcobol/gmath.cc
index c4670ff10b08..c6570f091a0d 100644
--- a/libgcobol/gmath.cc
+++ b/libgcobol/gmath.cc
@@ -253,24 +253,9 @@ subtraction_helper_float(GCOB_FP128 a_value,
   return a_value;
   }
 
-extern "C"
-void
-__gg__pow(  cbl_arith_format_t,
-            size_t,
-      const cblc_referlet_t *A,
-            size_t,
-      const cblc_referlet_t *B,
-            size_t,
-      const cblc_referlet_t *C,
-      const cbl_round_t  *rounded,
-            int           on_error_flag,
-            int          *compute_error
-            )
+static GCOB_FP128
+exponentiation_helper(GCOB_FP128 avalue, GCOB_FP128 bvalue, int *compute_error)
   {
-  GCOB_FP128 avalue =
-      __gg__float128_from_qualified_field(A[0].field, A[0].offset, A[0].size);
-  GCOB_FP128 bvalue =
-      __gg__float128_from_qualified_field(B[0].field, B[0].offset, B[0].size);
   GCOB_FP128 tgt_value;
 
   if( avalue == 0 && bvalue == 0 )
@@ -304,6 +289,30 @@ __gg__pow(  cbl_arith_format_t,
       tgt_value = 0;
       }
     }
+  return tgt_value;
+  }
+
+extern "C"
+void
+__gg__pow(  cbl_arith_format_t,
+            size_t,
+      const cblc_referlet_t *A,
+            size_t,
+      const cblc_referlet_t *B,
+            size_t,
+      const cblc_referlet_t *C,
+      const cbl_round_t  *rounded,
+            int           on_error_flag,
+            int          *compute_error
+            )
+  {
+  GCOB_FP128 avalue =
+      __gg__float128_from_qualified_field(A[0].field, A[0].offset, A[0].size);
+  GCOB_FP128 bvalue =
+      __gg__float128_from_qualified_field(B[0].field, B[0].offset, B[0].size);
+
+  GCOB_FP128 tgt_value = exponentiation_helper(avalue, bvalue, compute_error);
+
   if( !(*compute_error & compute_error_exp_minus_by_frac) )
     {
     *compute_error |= conditional_stash(C[0].field,
@@ -320,7 +329,7 @@ void
 __gg__process_compute_error(int compute_error)
   {
   // This routine gets called after a series of parser_op operations is
-  // complete (see parser_assign()) when the source code didn't specify
+ // complete (see parser_assign()) when the source code didn't specify
   // an ON SIZE ERROR clause.
   if( compute_error & compute_error_divide_by_zero)
     {
@@ -357,7 +366,7 @@ typedef struct int256
   {
   uint64_t i64[4];
   int rdigits;
-  }int256;
+  } int256;
 
 static inline uint64_t
 uint128_lo64(uint128 value)
@@ -1325,7 +1334,6 @@ void multiply_int128_by_int128(int256 &ABCD,
     negate_int256(ABCD);
     }
   ABCD.rdigits = ab_value.rdigits + cd_value.rdigits;
-
   }
 
 extern "C"
@@ -1754,7 +1762,6 @@ divide_int128_by_int128(int256   &quotient,
     }
   }
 
-
 extern "C"
 void
 __gg__dividef1_phase2(cbl_arith_format_t ,
@@ -2115,3 +2122,442 @@ __gg__dividef45(cbl_arith_format_t ,
     }
   }
 
+static int
+multiply_int256_by_int256(int256 &left, const int256 &right)
+  {
+  int retval = 0;
+  // The inputs have both been squeezed into 128 bits.
+  int128 l128;
+  int128 r128;
+
+  l128.i128    = int256_get_u128(left, 0);
+  l128.rdigits = left.rdigits;
+
+  r128.i128   = int256_get_u128(right, 0);
+  r128.rdigits = right.rdigits;
+
+  // We need to make both 128-bit operands positive:
+  bool negative = false;
+  if( left.i64[3] & 0x8000000000000000ULL )
+    {
+    negative = !negative;
+    l128.i128 = ~l128.i128 + 1;
+    }
+  if( right.i64[3] & 0x8000000000000000ULL )
+    {
+    negative = !negative;
+    r128.i128 = ~r128.i128 + 1;
+    }
+
+  multiply_int128_by_int128(left, l128, r128);
+
+  if( negative )
+    {
+    // This code takes the two's complement of the 256-bit value:
+    left.i64[0] = ~left.i64[0];
+    left.i64[1] = ~left.i64[1];
+    left.i64[2] = ~left.i64[2];
+    left.i64[3] = ~left.i64[3];
+    // I usually eschew code like this.  But it's just too precious.
+    if(++left.i64[0] == 0)
+    if(++left.i64[1] == 0)
+    if(++left.i64[2] == 0)
+       ++left.i64[3];
+    }
+
+  return retval;
+  }
+
+static int
+divide_int256_by_int256(int256 &left, const int256 &right)
+  {
+  int retval = 0;
+  // The inputs have both been squeezed into 128 bits.
+  int128 l128;
+  int128 r128;
+
+  l128.i128    = int256_get_u128(left, 0);
+  l128.rdigits = left.rdigits;
+
+  r128.i128   = int256_get_u128(right, 0);
+  r128.rdigits = right.rdigits;
+
+  // We need to make both 128-bit operands positive:
+  bool negative = false;
+  if( left.i64[3] & 0x8000000000000000ULL )
+    {
+    negative = !negative;
+    l128.i128 = ~l128.i128 + 1;
+    }
+  if( right.i64[3] & 0x8000000000000000ULL )
+    {
+    negative = !negative;
+    r128.i128 = ~r128.i128 + 1;
+    }
+
+  divide_int128_by_int128(left,
+                          l128.i128,
+                          l128.rdigits,
+                          r128.i128,
+                          r128.rdigits,
+                          &retval);
+
+  if( negative )
+    {
+    // This code takes the two's complement of the 256-bit value:
+    left.i64[0] = ~left.i64[0];
+    left.i64[1] = ~left.i64[1];
+    left.i64[2] = ~left.i64[2];
+    left.i64[3] = ~left.i64[3];
+    // I usually eschew code like this.  But it's just too precious.
+    if(++left.i64[0] == 0)
+    if(++left.i64[1] == 0)
+    if(++left.i64[2] == 0)
+       ++left.i64[3];
+    }
+
+  return retval;
+  }
+
+static std::vector<GCOB_FP128> compute_float_stack;
+static std::vector<int256> compute_fixed_stack;
+
+static int
+compute_fixed_add()
+  {
+  // This is RPN at work, so stack[N-2] += stack[N-1] 
+  int retval = 0;
+
+  size_t level = compute_fixed_stack.size();
+  assert( level >= 2 );
+  int256 left  = compute_fixed_stack[level-2];
+  int256 right = compute_fixed_stack[level-1];
+
+  add_int256_to_int256(left, right);
+
+  compute_fixed_stack[level-2] = left;
+  compute_fixed_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_fixed_subtract()
+  {
+  int retval = 0;
+
+  size_t level = compute_fixed_stack.size();
+  assert( level >= 2 );
+  int256 left  = compute_fixed_stack[level-2];
+  int256 right = compute_fixed_stack[level-1];
+  subtract_int256_from_int256(left, right);
+  compute_fixed_stack[level-2] = left;
+  compute_fixed_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_fixed_multiply()
+  {
+  int retval = 0;
+
+  size_t level = compute_fixed_stack.size();
+  assert( level >= 2 );
+  int256 left  = compute_fixed_stack[level-2];
+  int256 right = compute_fixed_stack[level-1];
+
+  retval |= squeeze_int256(left);
+  retval |= squeeze_int256(right);
+  retval |= multiply_int256_by_int256(left, right);
+
+  compute_fixed_stack[level-2] = left;
+  compute_fixed_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_fixed_divide()
+  {
+  int retval = 0;
+
+  size_t level = compute_fixed_stack.size();
+  assert( level >= 2 );
+  int256 left  = compute_fixed_stack[level-2];
+  int256 right = compute_fixed_stack[level-1];
+
+  retval |= squeeze_int256(left);
+  retval |= squeeze_int256(right);
+  retval |= divide_int256_by_int256(left, right);
+
+  compute_fixed_stack[level-2] = left;
+  compute_fixed_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_fixed_negate()
+  {
+  int retval = 0;
+
+  assert( !compute_fixed_stack.empty() );
+  int256 &left = compute_fixed_stack.back();
+
+  left.i64[0] = ~left.i64[0];
+  left.i64[1] = ~left.i64[1];
+  left.i64[2] = ~left.i64[2];
+  left.i64[3] = ~left.i64[3];
+  // I usually eschew code like this.  But it's just too precious.
+  if(++left.i64[0] == 0)
+  if(++left.i64[1] == 0)
+  if(++left.i64[2] == 0)
+     ++left.i64[3];
+
+  return retval;
+  }
+
+static int
+compute_float_add()
+  {
+  // This is RPN at work, so stack[N-2] += stack[N-1] 
+  int retval = 0;
+
+  size_t level = compute_float_stack.size();
+  assert( level >= 2 );
+  GCOB_FP128 left  = compute_float_stack[level-2];
+  GCOB_FP128 right = compute_float_stack[level-1];
+
+  left = addition_helper_float(left, right, &retval);;
+
+  compute_float_stack[level-2] = left;
+  compute_float_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_float_subtract()
+  {
+  int retval = 0;
+
+  size_t level = compute_float_stack.size();
+  assert( level >= 2 );
+  GCOB_FP128 left  = compute_float_stack[level-2];
+  GCOB_FP128 right = compute_float_stack[level-1];
+
+  left = subtraction_helper_float(left, right, &retval);;
+
+  compute_float_stack[level-2] = left;
+  compute_float_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_float_multiply()
+  {
+  int retval = 0;
+
+  size_t level = compute_float_stack.size();
+  assert( level >= 2 );
+  GCOB_FP128 left  = compute_float_stack[level-2];
+  GCOB_FP128 right = compute_float_stack[level-1];
+
+  left = multiply_helper_float(left, right, &retval);
+
+  compute_float_stack[level-2] = left;
+  compute_float_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_float_divide()
+  {
+  int retval = 0;
+
+  size_t level = compute_float_stack.size();
+  assert( level >= 2 );
+  GCOB_FP128 left  = compute_float_stack[level-2];
+  GCOB_FP128 right = compute_float_stack[level-1];
+
+  left = divide_helper_float(left, right, &retval);
+
+  compute_float_stack[level-2] = left;
+  compute_float_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_float_pow()
+  {
+  int retval = 0;
+
+  size_t level = compute_float_stack.size();
+  assert( level >= 2 );
+  GCOB_FP128 left  = compute_float_stack[level-2];
+  GCOB_FP128 right = compute_float_stack[level-1];
+  
+  left = exponentiation_helper(left, right, &retval);
+
+  compute_float_stack[level-2] = left;
+  compute_float_stack.pop_back();
+  return retval;
+  }
+
+static int
+compute_float_negate()
+  {
+  int retval = 0;
+  assert( !compute_float_stack.empty() );
+  compute_float_stack.back() = -compute_float_stack.back();
+  return retval;
+  }
+
+extern "C"
+int
+__gg__compute_fixed(const char          opstring[],
+                    const cblc_field_t *fields[],
+                    const size_t        offsets[])
+  {
+  int retval = 0;
+  size_t noperations = strlen(opstring);
+
+  // We will compute in fixed-point
+  compute_fixed_stack.clear();
+
+  for(size_t i=0; i<noperations; i++)
+    {
+    char ch = opstring[i];
+    switch(ch)
+      {
+      case 'P':
+        {
+        // We push a value onto the stack:
+        int256 value;
+        get_int256_from_qualified_field(value,
+                                        fields[i],
+                                        offsets[i],
+                                        fields[i]->capacity);
+        compute_fixed_stack.push_back(value);
+        }
+      break;
+
+      case '+':
+        retval |= compute_fixed_add();
+        break;
+
+      case '-':
+        retval |= compute_fixed_subtract();
+        break;
+
+      case '*':
+        retval |= compute_fixed_multiply();
+        break;
+
+      case '/':
+        retval |= compute_fixed_divide();
+        break;
+
+      case '!':
+        retval |= compute_fixed_negate();
+        break;
+
+      case '^':
+        fprintf(stderr, "We shouldn't see an integer a^b compute\n");
+        abort();
+        break;
+      }
+    }
+
+  // The destination was added to the end of the lists
+  cblc_field_t *target = const_cast<cblc_field_t *>(fields[noperations]);
+  size_t target_offset = offsets[noperations];
+
+  assert(compute_fixed_stack.size() == 1);
+  int256 v256 = compute_fixed_stack.back();
+  int overflow = squeeze_int256(v256);
+  if( overflow )
+    {
+    retval |= compute_error_overflow;
+    }
+
+  __int128 value;
+  value   =  v256.i64[1];
+  value <<= 64;
+  value  +=  v256.i64[0];
+
+  __gg__int128_to_qualified_field(target,
+                                  target_offset,
+                                  target->capacity,
+                                  value,
+                                  v256.rdigits,
+                                  truncation_e,
+                                  &retval);
+  return retval;
+  }
+
+extern "C"
+int
+__gg__compute_float(const char          opstring[],
+                    const cblc_field_t *fields[],
+                    const size_t        offsets[])
+  {
+  int retval = 0;
+  size_t noperations = strlen(opstring);
+
+  // We will compute in fixed-point
+  compute_float_stack.clear();
+
+  for(size_t i=0; i<noperations; i++)
+    {
+    char ch = opstring[i];
+    switch(ch)
+      {
+      case 'P':
+        {
+        // We push a value onto the stack:
+        GCOB_FP128 value = 
+                      __gg__float128_from_qualified_field(fields[i],
+                                                          offsets[i],
+                                                          fields[i]->capacity);
+        compute_float_stack.push_back(value);
+        }
+      break;
+
+      case '+':
+        retval |= compute_float_add();
+        break;
+
+      case '-':
+        retval |= compute_float_subtract();
+        break;
+
+      case '*':
+        retval |= compute_float_multiply();
+        break;
+
+      case '/':
+        retval |= compute_float_divide();
+        break;
+
+      case '^':
+        retval |= compute_float_pow();
+        break;
+
+      case '!':
+        retval |= compute_float_negate();
+        break;
+
+      }
+    }
+
+  // The destination was added to the end of the lists
+  cblc_field_t *target = const_cast<cblc_field_t *>(fields[noperations]);
+  size_t target_offset = offsets[noperations];
+
+  assert(compute_float_stack.size() == 1);
+  GCOB_FP128 value = compute_float_stack.back();
+
+  __gg__float128_to_qualified_field(target,
+                                    target_offset,
+                                    value,
+                                    truncation_e,
+                                    &retval);
+  return retval;
+  }
diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc
index 70f4fdfa6199..a5c9b4b4ad16 100644
--- a/libgcobol/libgcobol.cc
+++ b/libgcobol/libgcobol.cc
@@ -14137,3 +14137,56 @@ __gg__set_exception_call(const cblc_field_t *field,
                                           field->capacity,
                                           &nbytes);
   }
+
+extern "C"
+int
+__gg__prohibited(const cblc_field_t *field, __int128 value)
+  {
+  // This is the test for ROUNDING MODE PROHIBITED.  Returns non-zero when
+  // value can't fit into field.
+  int retval;
+  if(value < 0)
+    {
+    value = -value;
+    }
+  char ach[128];
+  int index = 0;
+  // Convert value to a stream of "digits", low-order first.
+  while(value)
+    {
+    ach[index++] = value % 10;
+    value /= 10;
+    }
+  if( index <= field->digits )
+    {
+    // We peeled off index digits, and value can hold that many.
+    retval = 0;
+    }
+  else
+    {
+    // we know that index is greater than field->digits
+
+    // Count the number of low-order zeroes in ach[].
+    int trailing_zeroes = 0;
+    for(int i=0; i<index; i++)
+      {
+      if( ach[i] )
+        {
+        break;
+        }
+      trailing_zeroes += 1;
+      }
+    // Subtracting trailing zeroes from index gives us the number of non-zero
+    // digits in ach:
+    index -= trailing_zeroes;
+    if( index <= field->digits )
+      {
+      retval = 0;
+      }
+    else
+      {
+      retval = 1;
+      }
+    }
+  return retval;
+  }
diff --git a/libgcobol/libgcobol.h b/libgcobol/libgcobol.h
index 92e8c497c630..a4456bc4cf9e 100644
--- a/libgcobol/libgcobol.h
+++ b/libgcobol/libgcobol.h
@@ -109,7 +109,7 @@ typedef struct int128
   {
   __int128 i128;
   int      rdigits;
-  }int128;
+  } int128;
 
 extern "C" void __gg__clock_gettime(struct cbl_timespec *tp);

Reply via email to