1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.core.common.type; 24 25 import static jdk.vm.ci.meta.MetaUtil.getSimpleName; 26 27 import java.util.Arrays; 28 import java.util.Objects; 29 import java.util.function.Function; 30 31 import org.graalvm.compiler.core.common.calc.FloatConvert; 32 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Add; 33 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And; 34 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Div; 35 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul; 36 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or; 37 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem; 38 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Sub; 39 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Xor; 40 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow; 41 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.SignExtend; 42 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.ZeroExtend; 43 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shl; 44 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shr; 45 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.UShr; 46 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Abs; 47 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Neg; 48 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Not; 49 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Sqrt; 50 import org.graalvm.util.CollectionsUtil; 51 52 import jdk.vm.ci.meta.Constant; 53 import jdk.vm.ci.meta.JavaKind; 54 55 /** 56 * Information about arithmetic operations. 57 */ 58 public final class ArithmeticOpTable { 59 60 private final UnaryOp<Neg> neg; 61 private final BinaryOp<Add> add; 62 private final BinaryOp<Sub> sub; 63 64 private final BinaryOp<Mul> mul; 65 private final BinaryOp<Div> div; 66 private final BinaryOp<Rem> rem; 67 68 private final UnaryOp<Not> not; 69 private final BinaryOp<And> and; 70 private final BinaryOp<Or> or; 71 private final BinaryOp<Xor> xor; 72 73 private final ShiftOp<Shl> shl; 74 private final ShiftOp<Shr> shr; 75 private final ShiftOp<UShr> ushr; 76 77 private final UnaryOp<Abs> abs; 78 private final UnaryOp<Sqrt> sqrt; 79 80 private final IntegerConvertOp<ZeroExtend> zeroExtend; 81 private final IntegerConvertOp<SignExtend> signExtend; 82 private final IntegerConvertOp<Narrow> narrow; 83 84 private final FloatConvertOp[] floatConvert; 85 private final int hash; 86 87 public static ArithmeticOpTable forStamp(Stamp s) { 88 if (s instanceof ArithmeticStamp) { 89 return ((ArithmeticStamp) s).getOps(); 90 } else { 91 return EMPTY; 92 } 93 } 94 95 public static final ArithmeticOpTable EMPTY = new ArithmeticOpTable(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); 96 97 public interface ArithmeticOpWrapper { 98 99 <OP> UnaryOp<OP> wrapUnaryOp(UnaryOp<OP> op); 100 101 <OP> BinaryOp<OP> wrapBinaryOp(BinaryOp<OP> op); 102 103 <OP> ShiftOp<OP> wrapShiftOp(ShiftOp<OP> op); 104 105 <OP> IntegerConvertOp<OP> wrapIntegerConvertOp(IntegerConvertOp<OP> op); 106 107 FloatConvertOp wrapFloatConvertOp(FloatConvertOp op); 108 } 109 110 private static <T> T wrapIfNonNull(Function<T, T> wrapper, T obj) { 111 if (obj == null) { 112 return null; 113 } else { 114 return wrapper.apply(obj); 115 } 116 } 117 118 public static ArithmeticOpTable wrap(ArithmeticOpWrapper wrapper, ArithmeticOpTable inner) { 119 UnaryOp<Neg> neg = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getNeg()); 120 BinaryOp<Add> add = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getAdd()); 121 BinaryOp<Sub> sub = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getSub()); 122 123 BinaryOp<Mul> mul = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMul()); 124 BinaryOp<Div> div = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getDiv()); 125 BinaryOp<Rem> rem = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getRem()); 126 127 UnaryOp<Not> not = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getNot()); 128 BinaryOp<And> and = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getAnd()); 129 BinaryOp<Or> or = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getOr()); 130 BinaryOp<Xor> xor = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getXor()); 131 132 ShiftOp<Shl> shl = wrapIfNonNull(wrapper::wrapShiftOp, inner.getShl()); 133 ShiftOp<Shr> shr = wrapIfNonNull(wrapper::wrapShiftOp, inner.getShr()); 134 ShiftOp<UShr> ushr = wrapIfNonNull(wrapper::wrapShiftOp, inner.getUShr()); 135 136 UnaryOp<Abs> abs = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getAbs()); 137 UnaryOp<Sqrt> sqrt = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getSqrt()); 138 139 IntegerConvertOp<ZeroExtend> zeroExtend = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getZeroExtend()); 140 IntegerConvertOp<SignExtend> signExtend = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getSignExtend()); 141 IntegerConvertOp<Narrow> narrow = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getNarrow()); 142 143 FloatConvertOp[] floatConvert = CollectionsUtil.filterAndMapToArray(inner.floatConvert, Objects::nonNull, wrapper::wrapFloatConvertOp, FloatConvertOp[]::new); 144 return new ArithmeticOpTable(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, floatConvert); 145 } 146 147 protected ArithmeticOpTable(UnaryOp<Neg> neg, BinaryOp<Add> add, BinaryOp<Sub> sub, BinaryOp<Mul> mul, BinaryOp<Div> div, BinaryOp<Rem> rem, UnaryOp<Not> not, BinaryOp<And> and, BinaryOp<Or> or, 148 BinaryOp<Xor> xor, ShiftOp<Shl> shl, ShiftOp<Shr> shr, ShiftOp<UShr> ushr, UnaryOp<Abs> abs, UnaryOp<Sqrt> sqrt, IntegerConvertOp<ZeroExtend> zeroExtend, 149 IntegerConvertOp<SignExtend> signExtend, IntegerConvertOp<Narrow> narrow, FloatConvertOp... floatConvert) { 150 this.neg = neg; 151 this.add = add; 152 this.sub = sub; 153 this.mul = mul; 154 this.div = div; 155 this.rem = rem; 156 this.not = not; 157 this.and = and; 158 this.or = or; 159 this.xor = xor; 160 this.shl = shl; 161 this.shr = shr; 162 this.ushr = ushr; 163 this.abs = abs; 164 this.sqrt = sqrt; 165 this.zeroExtend = zeroExtend; 166 this.signExtend = signExtend; 167 this.narrow = narrow; 168 this.floatConvert = new FloatConvertOp[FloatConvert.values().length]; 169 for (FloatConvertOp op : floatConvert) { 170 this.floatConvert[op.getFloatConvert().ordinal()] = op; 171 } 172 173 this.hash = Objects.hash(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow); 174 } 175 176 @Override 177 public int hashCode() { 178 return hash; 179 } 180 181 /** 182 * Describes the unary negation operation. 183 */ 184 public UnaryOp<Neg> getNeg() { 185 return neg; 186 } 187 188 /** 189 * Describes the addition operation. 190 */ 191 public BinaryOp<Add> getAdd() { 192 return add; 193 } 194 195 /** 196 * Describes the subtraction operation. 197 */ 198 public BinaryOp<Sub> getSub() { 199 return sub; 200 } 201 202 /** 203 * Describes the multiplication operation. 204 */ 205 public BinaryOp<Mul> getMul() { 206 return mul; 207 } 208 209 /** 210 * Describes the division operation. 211 */ 212 public BinaryOp<Div> getDiv() { 213 return div; 214 } 215 216 /** 217 * Describes the remainder operation. 218 */ 219 public BinaryOp<Rem> getRem() { 220 return rem; 221 } 222 223 /** 224 * Describes the bitwise not operation. 225 */ 226 public UnaryOp<Not> getNot() { 227 return not; 228 } 229 230 /** 231 * Describes the bitwise and operation. 232 */ 233 public BinaryOp<And> getAnd() { 234 return and; 235 } 236 237 /** 238 * Describes the bitwise or operation. 239 */ 240 public BinaryOp<Or> getOr() { 241 return or; 242 } 243 244 /** 245 * Describes the bitwise xor operation. 246 */ 247 public BinaryOp<Xor> getXor() { 248 return xor; 249 } 250 251 /** 252 * Describes the shift left operation. 253 */ 254 public ShiftOp<Shl> getShl() { 255 return shl; 256 } 257 258 /** 259 * Describes the signed shift right operation. 260 */ 261 public ShiftOp<Shr> getShr() { 262 return shr; 263 } 264 265 /** 266 * Describes the unsigned shift right operation. 267 */ 268 public ShiftOp<UShr> getUShr() { 269 return ushr; 270 } 271 272 /** 273 * Describes the absolute value operation. 274 */ 275 public UnaryOp<Abs> getAbs() { 276 return abs; 277 } 278 279 /** 280 * Describes the square root operation. 281 */ 282 public UnaryOp<Sqrt> getSqrt() { 283 return sqrt; 284 } 285 286 /** 287 * Describes the zero extend conversion. 288 */ 289 public IntegerConvertOp<ZeroExtend> getZeroExtend() { 290 return zeroExtend; 291 } 292 293 /** 294 * Describes the sign extend conversion. 295 */ 296 public IntegerConvertOp<SignExtend> getSignExtend() { 297 return signExtend; 298 } 299 300 /** 301 * Describes the narrowing conversion. 302 */ 303 public IntegerConvertOp<Narrow> getNarrow() { 304 return narrow; 305 } 306 307 /** 308 * Describes integer/float/double conversions. 309 */ 310 public FloatConvertOp getFloatConvert(FloatConvert op) { 311 return floatConvert[op.ordinal()]; 312 } 313 314 public static String toString(Op... ops) { 315 return CollectionsUtil.mapAndJoin(ops, o -> o == null ? "null" : o.operator + "{" + getSimpleName(o.getClass(), false) + "}", ","); 316 } 317 318 private boolean opsEquals(ArithmeticOpTable that) { 319 // @formatter:off 320 return Objects.equals(neg, that.neg) && 321 Objects.equals(add, that.add) && 322 Objects.equals(sub, that.sub) && 323 Objects.equals(mul, that.mul) && 324 Objects.equals(div, that.div) && 325 Objects.equals(rem, that.rem) && 326 Objects.equals(not, that.not) && 327 Objects.equals(and, that.and) && 328 Objects.equals(or, that.or) && 329 Objects.equals(xor, that.xor) && 330 Objects.equals(shl, that.shl) && 331 Objects.equals(shr, that.shr) && 332 Objects.equals(ushr, that.ushr) && 333 Objects.equals(abs, that.abs) && 334 Objects.equals(sqrt, that.sqrt) && 335 Objects.equals(zeroExtend, that.zeroExtend) && 336 Objects.equals(signExtend, that.signExtend) && 337 Objects.equals(narrow, that.narrow); 338 // @formatter:on 339 } 340 341 @Override 342 public boolean equals(Object obj) { 343 if (this == obj) { 344 return true; 345 } 346 if (obj == null) { 347 return false; 348 } 349 if (getClass() != obj.getClass()) { 350 return false; 351 } 352 ArithmeticOpTable that = (ArithmeticOpTable) obj; 353 if (opsEquals(that)) { 354 if (Arrays.equals(this.floatConvert, that.floatConvert)) { 355 return true; 356 } 357 } 358 return false; 359 } 360 361 @Override 362 public String toString() { 363 return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) + ",floatConvert[" + 364 toString(floatConvert) + "]]"; 365 } 366 367 public abstract static class Op { 368 369 private final String operator; 370 371 protected Op(String operator) { 372 this.operator = operator; 373 } 374 375 @Override 376 public String toString() { 377 return operator; 378 } 379 380 @Override 381 public int hashCode() { 382 return operator.hashCode(); 383 } 384 385 @Override 386 public boolean equals(Object obj) { 387 if (this == obj) { 388 return true; 389 } 390 if (obj == null) { 391 return false; 392 } 393 if (getClass() != obj.getClass()) { 394 return false; 395 } 396 Op that = (Op) obj; 397 if (operator.equals(that.operator)) { 398 return true; 399 } 400 return true; 401 } 402 } 403 404 /** 405 * Describes a unary arithmetic operation. 406 */ 407 public abstract static class UnaryOp<T> extends Op { 408 409 public abstract static class Neg extends UnaryOp<Neg> { 410 411 protected Neg() { 412 super("-"); 413 } 414 } 415 416 public abstract static class Not extends UnaryOp<Not> { 417 418 protected Not() { 419 super("~"); 420 } 421 } 422 423 public abstract static class Abs extends UnaryOp<Abs> { 424 425 protected Abs() { 426 super("ABS"); 427 } 428 } 429 430 public abstract static class Sqrt extends UnaryOp<Sqrt> { 431 432 protected Sqrt() { 433 super("SQRT"); 434 } 435 } 436 437 protected UnaryOp(String operation) { 438 super(operation); 439 } 440 441 /** 442 * Apply the operation to a {@link Constant}. 443 */ 444 public abstract Constant foldConstant(Constant value); 445 446 /** 447 * Apply the operation to a {@link Stamp}. 448 */ 449 public abstract Stamp foldStamp(Stamp stamp); 450 451 public UnaryOp<T> unwrap() { 452 return this; 453 } 454 } 455 456 /** 457 * Describes a binary arithmetic operation. 458 */ 459 public abstract static class BinaryOp<T> extends Op { 460 461 public abstract static class Add extends BinaryOp<Add> { 462 463 protected Add(boolean associative, boolean commutative) { 464 super("+", associative, commutative); 465 } 466 } 467 468 public abstract static class Sub extends BinaryOp<Sub> { 469 470 protected Sub(boolean associative, boolean commutative) { 471 super("-", associative, commutative); 472 } 473 } 474 475 public abstract static class Mul extends BinaryOp<Mul> { 476 477 protected Mul(boolean associative, boolean commutative) { 478 super("*", associative, commutative); 479 } 480 } 481 482 public abstract static class Div extends BinaryOp<Div> { 483 484 protected Div(boolean associative, boolean commutative) { 485 super("/", associative, commutative); 486 } 487 } 488 489 public abstract static class Rem extends BinaryOp<Rem> { 490 491 protected Rem(boolean associative, boolean commutative) { 492 super("%", associative, commutative); 493 } 494 } 495 496 public abstract static class And extends BinaryOp<And> { 497 498 protected And(boolean associative, boolean commutative) { 499 super("&", associative, commutative); 500 } 501 } 502 503 public abstract static class Or extends BinaryOp<Or> { 504 505 protected Or(boolean associative, boolean commutative) { 506 super("|", associative, commutative); 507 } 508 } 509 510 public abstract static class Xor extends BinaryOp<Xor> { 511 512 protected Xor(boolean associative, boolean commutative) { 513 super("^", associative, commutative); 514 } 515 } 516 517 private final boolean associative; 518 private final boolean commutative; 519 520 protected BinaryOp(String operation, boolean associative, boolean commutative) { 521 super(operation); 522 this.associative = associative; 523 this.commutative = commutative; 524 } 525 526 /** 527 * Apply the operation to two {@linkplain Constant Constants}. 528 */ 529 public abstract Constant foldConstant(Constant a, Constant b); 530 531 /** 532 * Apply the operation to two {@linkplain Stamp Stamps}. 533 */ 534 public abstract Stamp foldStamp(Stamp a, Stamp b); 535 536 /** 537 * Checks whether this operation is associative. An operation is associative when 538 * {@code (a . b) . c == a . (b . c)} for all a, b, c. Note that you still have to be 539 * careful with inverses. For example the integer subtraction operation will report 540 * {@code true} here, since you can still reassociate as long as the correct negations are 541 * inserted. 542 */ 543 public final boolean isAssociative() { 544 return associative; 545 } 546 547 /** 548 * Checks whether this operation is commutative. An operation is commutative when 549 * {@code a . b == b . a} for all a, b. 550 */ 551 public final boolean isCommutative() { 552 return commutative; 553 } 554 555 /** 556 * Check whether a {@link Constant} is a neutral element for this operation. A neutral 557 * element is any element {@code n} where {@code a . n == a} for all a. 558 * 559 * @param n the {@link Constant} that should be tested 560 * @return true iff for all {@code a}: {@code a . n == a} 561 */ 562 public boolean isNeutral(Constant n) { 563 return false; 564 } 565 566 /** 567 * Check whether this operation has a zero {@code z == a . a} for each a. Examples of 568 * operations having such an element are subtraction and exclusive-or. Note that this may be 569 * different from the numbers tested by {@link #isNeutral}. 570 * 571 * @param stamp a {@link Stamp} 572 * @return a unique {@code z} such that {@code z == a . a} for each {@code a} in 573 * {@code stamp} if it exists, otherwise {@code null} 574 */ 575 public Constant getZero(Stamp stamp) { 576 return null; 577 } 578 579 public BinaryOp<T> unwrap() { 580 return this; 581 } 582 583 @Override 584 public int hashCode() { 585 final int prime = 31; 586 int result = super.hashCode(); 587 result = prime * result + (associative ? 1231 : 1237); 588 result = prime * result + (commutative ? 1231 : 1237); 589 return result; 590 } 591 592 @Override 593 public boolean equals(Object obj) { 594 if (this == obj) { 595 return true; 596 } 597 if (!super.equals(obj)) { 598 return false; 599 } 600 if (getClass() != obj.getClass()) { 601 return false; 602 } 603 BinaryOp<?> that = (BinaryOp<?>) obj; 604 if (associative != that.associative) { 605 return false; 606 } 607 if (commutative != that.commutative) { 608 return false; 609 } 610 return true; 611 } 612 613 @Override 614 public String toString() { 615 if (associative) { 616 if (commutative) { 617 return super.toString() + "[AC]"; 618 } else { 619 return super.toString() + "[A]"; 620 } 621 } else if (commutative) { 622 return super.toString() + "[C]"; 623 } 624 return super.toString(); 625 } 626 } 627 628 /** 629 * Describes a shift operation. The right argument of a shift operation always has kind 630 * {@link JavaKind#Int}. 631 */ 632 public abstract static class ShiftOp<OP> extends Op { 633 634 public abstract static class Shl extends ShiftOp<Shl> { 635 636 public Shl() { 637 super("<<"); 638 } 639 } 640 641 public abstract static class Shr extends ShiftOp<Shr> { 642 643 public Shr() { 644 super(">>"); 645 } 646 } 647 648 public abstract static class UShr extends ShiftOp<UShr> { 649 650 public UShr() { 651 super(">>>"); 652 } 653 } 654 655 protected ShiftOp(String operation) { 656 super(operation); 657 } 658 659 /** 660 * Apply the shift to a constant. 661 */ 662 public abstract Constant foldConstant(Constant c, int amount); 663 664 /** 665 * Apply the shift to a stamp. 666 */ 667 public abstract Stamp foldStamp(Stamp s, IntegerStamp amount); 668 669 /** 670 * Get the shift amount mask for a given result stamp. 671 */ 672 public abstract int getShiftAmountMask(Stamp s); 673 } 674 675 public abstract static class FloatConvertOp extends UnaryOp<FloatConvertOp> { 676 677 private final FloatConvert op; 678 679 protected FloatConvertOp(FloatConvert op) { 680 super(op.name()); 681 this.op = op; 682 } 683 684 public FloatConvert getFloatConvert() { 685 return op; 686 } 687 688 @Override 689 public FloatConvertOp unwrap() { 690 return this; 691 } 692 693 @Override 694 public int hashCode() { 695 final int prime = 31; 696 return prime * super.hashCode() + op.hashCode(); 697 } 698 699 @Override 700 public boolean equals(Object obj) { 701 if (this == obj) { 702 return true; 703 } 704 if (!super.equals(obj)) { 705 return false; 706 } 707 if (getClass() != obj.getClass()) { 708 return false; 709 } 710 FloatConvertOp that = (FloatConvertOp) obj; 711 if (op != that.op) { 712 return false; 713 } 714 return true; 715 } 716 } 717 718 public abstract static class IntegerConvertOp<T> extends Op { 719 720 public abstract static class ZeroExtend extends IntegerConvertOp<ZeroExtend> { 721 722 protected ZeroExtend() { 723 super("ZeroExtend"); 724 } 725 } 726 727 public abstract static class SignExtend extends IntegerConvertOp<SignExtend> { 728 729 protected SignExtend() { 730 super("SignExtend"); 731 } 732 } 733 734 public abstract static class Narrow extends IntegerConvertOp<Narrow> { 735 736 protected Narrow() { 737 super("Narrow"); 738 } 739 740 @Override 741 public Stamp invertStamp(int inputBits, int resultBits, Stamp outStamp) { 742 return null; 743 } 744 } 745 746 protected IntegerConvertOp(String op) { 747 super(op); 748 } 749 750 public abstract Constant foldConstant(int inputBits, int resultBits, Constant value); 751 752 public abstract Stamp foldStamp(int inputBits, int resultBits, Stamp stamp); 753 754 public IntegerConvertOp<T> unwrap() { 755 return this; 756 } 757 758 /** 759 * Computes the stamp of the input for the given output stamp. 760 */ 761 public abstract Stamp invertStamp(int inputBits, int resultBits, Stamp outStamp); 762 } 763 }