brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.8 KiB · 5c68236 Raw
550 lines · cpp
1//===- ArithToAPFloat.cpp - Arithmetic to APFloat Conversion --------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Conversion/ArithToAPFloat/ArithToAPFloat.h"10 11#include "mlir/Dialect/Arith/IR/Arith.h"12#include "mlir/Dialect/Arith/Transforms/Passes.h"13#include "mlir/Dialect/Func/IR/FuncOps.h"14#include "mlir/Dialect/Func/Utils/Utils.h"15#include "mlir/IR/PatternMatch.h"16#include "mlir/IR/Verifier.h"17#include "mlir/Transforms/WalkPatternRewriteDriver.h"18 19namespace mlir {20#define GEN_PASS_DEF_ARITHTOAPFLOATCONVERSIONPASS21#include "mlir/Conversion/Passes.h.inc"22} // namespace mlir23 24using namespace mlir;25using namespace mlir::func;26 27static FuncOp createFnDecl(OpBuilder &b, SymbolOpInterface symTable,28                           StringRef name, FunctionType funcT, bool setPrivate,29                           SymbolTableCollection *symbolTables = nullptr) {30  OpBuilder::InsertionGuard g(b);31  assert(!symTable->getRegion(0).empty() && "expected non-empty region");32  b.setInsertionPointToStart(&symTable->getRegion(0).front());33  FuncOp funcOp = FuncOp::create(b, symTable->getLoc(), name, funcT);34  if (setPrivate)35    funcOp.setPrivate();36  if (symbolTables) {37    SymbolTable &symbolTable = symbolTables->getSymbolTable(symTable);38    symbolTable.insert(funcOp, symTable->getRegion(0).front().begin());39  }40  return funcOp;41}42 43/// Helper function to look up or create the symbol for a runtime library44/// function with the given parameter types. Returns an int64_t, unless a45/// different result type is specified.46static FailureOr<FuncOp>47lookupOrCreateApFloatFn(OpBuilder &b, SymbolOpInterface symTable,48                        StringRef name, TypeRange paramTypes,49                        SymbolTableCollection *symbolTables = nullptr,50                        Type resultType = {}) {51  if (!resultType)52    resultType = IntegerType::get(symTable->getContext(), 64);53  std::string funcName = (llvm::Twine("_mlir_apfloat_") + name).str();54  auto funcT = FunctionType::get(b.getContext(), paramTypes, {resultType});55  FailureOr<FuncOp> func =56      lookupFnDecl(symTable, funcName, funcT, symbolTables);57  // Failed due to type mismatch.58  if (failed(func))59    return func;60  // Successfully matched existing decl.61  if (*func)62    return *func;63 64  return createFnDecl(b, symTable, funcName, funcT,65                      /*setPrivate=*/true, symbolTables);66}67 68/// Helper function to look up or create the symbol for a runtime library69/// function for a binary arithmetic operation.70///71/// Parameter 1: APFloat semantics72/// Parameter 2: Left-hand side operand73/// Parameter 3: Right-hand side operand74///75/// This function will return a failure if the function is found but has an76/// unexpected signature.77///78static FailureOr<FuncOp>79lookupOrCreateBinaryFn(OpBuilder &b, SymbolOpInterface symTable, StringRef name,80                       SymbolTableCollection *symbolTables = nullptr) {81  auto i32Type = IntegerType::get(symTable->getContext(), 32);82  auto i64Type = IntegerType::get(symTable->getContext(), 64);83  return lookupOrCreateApFloatFn(b, symTable, name, {i32Type, i64Type, i64Type},84                                 symbolTables);85}86 87static Value getSemanticsValue(OpBuilder &b, Location loc, FloatType floatTy) {88  int32_t sem = llvm::APFloatBase::SemanticsToEnum(floatTy.getFloatSemantics());89  return arith::ConstantOp::create(b, loc, b.getI32Type(),90                                   b.getIntegerAttr(b.getI32Type(), sem));91}92 93/// Rewrite a binary arithmetic operation to an APFloat function call.94template <typename OpTy>95struct BinaryArithOpToAPFloatConversion final : OpRewritePattern<OpTy> {96  BinaryArithOpToAPFloatConversion(MLIRContext *context,97                                   const char *APFloatName,98                                   SymbolOpInterface symTable,99                                   PatternBenefit benefit = 1)100      : OpRewritePattern<OpTy>(context, benefit), symTable(symTable),101        APFloatName(APFloatName) {};102 103  LogicalResult matchAndRewrite(OpTy op,104                                PatternRewriter &rewriter) const override {105    // Get APFloat function from runtime library.106    FailureOr<FuncOp> fn =107        lookupOrCreateBinaryFn(rewriter, symTable, APFloatName);108    if (failed(fn))109      return fn;110 111    rewriter.setInsertionPoint(op);112    // Cast operands to 64-bit integers.113    Location loc = op.getLoc();114    auto floatTy = cast<FloatType>(op.getType());115    auto intWType = rewriter.getIntegerType(floatTy.getWidth());116    auto int64Type = rewriter.getI64Type();117    Value lhsBits = arith::ExtUIOp::create(118        rewriter, loc, int64Type,119        arith::BitcastOp::create(rewriter, loc, intWType, op.getLhs()));120    Value rhsBits = arith::ExtUIOp::create(121        rewriter, loc, int64Type,122        arith::BitcastOp::create(rewriter, loc, intWType, op.getRhs()));123 124    // Call APFloat function.125    Value semValue = getSemanticsValue(rewriter, loc, floatTy);126    SmallVector<Value> params = {semValue, lhsBits, rhsBits};127    auto resultOp =128        func::CallOp::create(rewriter, loc, TypeRange(rewriter.getI64Type()),129                             SymbolRefAttr::get(*fn), params);130 131    // Truncate result to the original width.132    Value truncatedBits = arith::TruncIOp::create(rewriter, loc, intWType,133                                                  resultOp->getResult(0));134    rewriter.replaceOp(135        op, arith::BitcastOp::create(rewriter, loc, floatTy, truncatedBits));136    return success();137  }138 139  SymbolOpInterface symTable;140  const char *APFloatName;141};142 143template <typename OpTy>144struct FpToFpConversion final : OpRewritePattern<OpTy> {145  FpToFpConversion(MLIRContext *context, SymbolOpInterface symTable,146                   PatternBenefit benefit = 1)147      : OpRewritePattern<OpTy>(context, benefit), symTable(symTable) {}148 149  LogicalResult matchAndRewrite(OpTy op,150                                PatternRewriter &rewriter) const override {151    // Get APFloat function from runtime library.152    auto i32Type = IntegerType::get(symTable->getContext(), 32);153    auto i64Type = IntegerType::get(symTable->getContext(), 64);154    FailureOr<FuncOp> fn = lookupOrCreateApFloatFn(155        rewriter, symTable, "convert", {i32Type, i32Type, i64Type});156    if (failed(fn))157      return fn;158 159    rewriter.setInsertionPoint(op);160    // Cast operands to 64-bit integers.161    Location loc = op.getLoc();162    auto inFloatTy = cast<FloatType>(op.getOperand().getType());163    auto inIntWType = rewriter.getIntegerType(inFloatTy.getWidth());164    Value operandBits = arith::ExtUIOp::create(165        rewriter, loc, i64Type,166        arith::BitcastOp::create(rewriter, loc, inIntWType, op.getOperand()));167 168    // Call APFloat function.169    Value inSemValue = getSemanticsValue(rewriter, loc, inFloatTy);170    auto outFloatTy = cast<FloatType>(op.getType());171    Value outSemValue = getSemanticsValue(rewriter, loc, outFloatTy);172    std::array<Value, 3> params = {inSemValue, outSemValue, operandBits};173    auto resultOp =174        func::CallOp::create(rewriter, loc, TypeRange(rewriter.getI64Type()),175                             SymbolRefAttr::get(*fn), params);176 177    // Truncate result to the original width.178    auto outIntWType = rewriter.getIntegerType(outFloatTy.getWidth());179    Value truncatedBits = arith::TruncIOp::create(rewriter, loc, outIntWType,180                                                  resultOp->getResult(0));181    rewriter.replaceOp(182        op, arith::BitcastOp::create(rewriter, loc, outFloatTy, truncatedBits));183    return success();184  }185 186  SymbolOpInterface symTable;187};188 189template <typename OpTy>190struct FpToIntConversion final : OpRewritePattern<OpTy> {191  FpToIntConversion(MLIRContext *context, SymbolOpInterface symTable,192                    bool isUnsigned, PatternBenefit benefit = 1)193      : OpRewritePattern<OpTy>(context, benefit), symTable(symTable),194        isUnsigned(isUnsigned) {}195 196  LogicalResult matchAndRewrite(OpTy op,197                                PatternRewriter &rewriter) const override {198    if (op.getType().getIntOrFloatBitWidth() > 64)199      return rewriter.notifyMatchFailure(200          op, "result type > 64 bits is not supported");201 202    // Get APFloat function from runtime library.203    auto i1Type = IntegerType::get(symTable->getContext(), 1);204    auto i32Type = IntegerType::get(symTable->getContext(), 32);205    auto i64Type = IntegerType::get(symTable->getContext(), 64);206    FailureOr<FuncOp> fn =207        lookupOrCreateApFloatFn(rewriter, symTable, "convert_to_int",208                                {i32Type, i32Type, i1Type, i64Type});209    if (failed(fn))210      return fn;211 212    rewriter.setInsertionPoint(op);213    // Cast operands to 64-bit integers.214    Location loc = op.getLoc();215    auto inFloatTy = cast<FloatType>(op.getOperand().getType());216    auto inIntWType = rewriter.getIntegerType(inFloatTy.getWidth());217    Value operandBits = arith::ExtUIOp::create(218        rewriter, loc, i64Type,219        arith::BitcastOp::create(rewriter, loc, inIntWType, op.getOperand()));220 221    // Call APFloat function.222    Value inSemValue = getSemanticsValue(rewriter, loc, inFloatTy);223    auto outIntTy = cast<IntegerType>(op.getType());224    Value outWidthValue = arith::ConstantOp::create(225        rewriter, loc, i32Type,226        rewriter.getIntegerAttr(i32Type, outIntTy.getWidth()));227    Value isUnsignedValue = arith::ConstantOp::create(228        rewriter, loc, i1Type, rewriter.getIntegerAttr(i1Type, isUnsigned));229    SmallVector<Value> params = {inSemValue, outWidthValue, isUnsignedValue,230                                 operandBits};231    auto resultOp =232        func::CallOp::create(rewriter, loc, TypeRange(rewriter.getI64Type()),233                             SymbolRefAttr::get(*fn), params);234 235    // Truncate result to the original width.236    Value truncatedBits = arith::TruncIOp::create(rewriter, loc, outIntTy,237                                                  resultOp->getResult(0));238    rewriter.replaceOp(op, truncatedBits);239    return success();240  }241 242  SymbolOpInterface symTable;243  bool isUnsigned;244};245 246template <typename OpTy>247struct IntToFpConversion final : OpRewritePattern<OpTy> {248  IntToFpConversion(MLIRContext *context, SymbolOpInterface symTable,249                    bool isUnsigned, PatternBenefit benefit = 1)250      : OpRewritePattern<OpTy>(context, benefit), symTable(symTable),251        isUnsigned(isUnsigned) {}252 253  LogicalResult matchAndRewrite(OpTy op,254                                PatternRewriter &rewriter) const override {255    Location loc = op.getLoc();256    if (op.getIn().getType().getIntOrFloatBitWidth() > 64) {257      return rewriter.notifyMatchFailure(258          loc, "integer bitwidth > 64 is not supported");259    }260 261    // Get APFloat function from runtime library.262    auto i1Type = IntegerType::get(symTable->getContext(), 1);263    auto i32Type = IntegerType::get(symTable->getContext(), 32);264    auto i64Type = IntegerType::get(symTable->getContext(), 64);265    FailureOr<FuncOp> fn =266        lookupOrCreateApFloatFn(rewriter, symTable, "convert_from_int",267                                {i32Type, i32Type, i1Type, i64Type});268    if (failed(fn))269      return fn;270 271    rewriter.setInsertionPoint(op);272    // Cast operands to 64-bit integers.273    auto inIntTy = cast<IntegerType>(op.getOperand().getType());274    Value operandBits = op.getOperand();275    if (operandBits.getType().getIntOrFloatBitWidth() < 64) {276      if (isUnsigned) {277        operandBits =278            arith::ExtUIOp::create(rewriter, loc, i64Type, operandBits);279      } else {280        operandBits =281            arith::ExtSIOp::create(rewriter, loc, i64Type, operandBits);282      }283    }284 285    // Call APFloat function.286    auto outFloatTy = cast<FloatType>(op.getType());287    Value outSemValue = getSemanticsValue(rewriter, loc, outFloatTy);288    Value inWidthValue = arith::ConstantOp::create(289        rewriter, loc, i32Type,290        rewriter.getIntegerAttr(i32Type, inIntTy.getWidth()));291    Value isUnsignedValue = arith::ConstantOp::create(292        rewriter, loc, i1Type, rewriter.getIntegerAttr(i1Type, isUnsigned));293    SmallVector<Value> params = {outSemValue, inWidthValue, isUnsignedValue,294                                 operandBits};295    auto resultOp =296        func::CallOp::create(rewriter, loc, TypeRange(rewriter.getI64Type()),297                             SymbolRefAttr::get(*fn), params);298 299    // Truncate result to the original width.300    auto outIntWType = rewriter.getIntegerType(outFloatTy.getWidth());301    Value truncatedBits = arith::TruncIOp::create(rewriter, loc, outIntWType,302                                                  resultOp->getResult(0));303    Value result =304        arith::BitcastOp::create(rewriter, loc, outFloatTy, truncatedBits);305    rewriter.replaceOp(op, result);306    return success();307  }308 309  SymbolOpInterface symTable;310  bool isUnsigned;311};312 313struct CmpFOpToAPFloatConversion final : OpRewritePattern<arith::CmpFOp> {314  CmpFOpToAPFloatConversion(MLIRContext *context, SymbolOpInterface symTable,315                            PatternBenefit benefit = 1)316      : OpRewritePattern<arith::CmpFOp>(context, benefit), symTable(symTable) {}317 318  LogicalResult matchAndRewrite(arith::CmpFOp op,319                                PatternRewriter &rewriter) const override {320    // Get APFloat function from runtime library.321    auto i1Type = IntegerType::get(symTable->getContext(), 1);322    auto i8Type = IntegerType::get(symTable->getContext(), 8);323    auto i32Type = IntegerType::get(symTable->getContext(), 32);324    auto i64Type = IntegerType::get(symTable->getContext(), 64);325    FailureOr<FuncOp> fn =326        lookupOrCreateApFloatFn(rewriter, symTable, "compare",327                                {i32Type, i64Type, i64Type}, nullptr, i8Type);328    if (failed(fn))329      return fn;330 331    // Cast operands to 64-bit integers.332    rewriter.setInsertionPoint(op);333    Location loc = op.getLoc();334    auto floatTy = cast<FloatType>(op.getLhs().getType());335    auto intWType = rewriter.getIntegerType(floatTy.getWidth());336    Value lhsBits = arith::ExtUIOp::create(337        rewriter, loc, i64Type,338        arith::BitcastOp::create(rewriter, loc, intWType, op.getLhs()));339    Value rhsBits = arith::ExtUIOp::create(340        rewriter, loc, i64Type,341        arith::BitcastOp::create(rewriter, loc, intWType, op.getRhs()));342 343    // Call APFloat function.344    Value semValue = getSemanticsValue(rewriter, loc, floatTy);345    SmallVector<Value> params = {semValue, lhsBits, rhsBits};346    Value comparisonResult =347        func::CallOp::create(rewriter, loc, TypeRange(i8Type),348                             SymbolRefAttr::get(*fn), params)349            ->getResult(0);350 351    // Generate an i1 SSA value that is "true" if the comparison result matches352    // the given `val`.353    auto checkResult = [&](llvm::APFloat::cmpResult val) {354      return arith::CmpIOp::create(355          rewriter, loc, arith::CmpIPredicate::eq, comparisonResult,356          arith::ConstantOp::create(357              rewriter, loc, i8Type,358              rewriter.getIntegerAttr(i8Type, static_cast<int8_t>(val)))359              .getResult());360    };361    // Generate an i1 SSA value that is "true" if the comparison result matches362    // any of the given `vals`.363    std::function<Value(ArrayRef<llvm::APFloat::cmpResult>)> checkResults =364        [&](ArrayRef<llvm::APFloat::cmpResult> vals) {365          Value first = checkResult(vals.front());366          if (vals.size() == 1)367            return first;368          Value rest = checkResults(vals.drop_front());369          return arith::OrIOp::create(rewriter, loc, first, rest).getResult();370        };371 372    // This switch-case statement was taken from arith::applyCmpPredicate.373    Value result;374    switch (op.getPredicate()) {375    case arith::CmpFPredicate::AlwaysFalse:376      result = arith::ConstantOp::create(rewriter, loc, i1Type,377                                         rewriter.getIntegerAttr(i1Type, 0))378                   .getResult();379      break;380    case arith::CmpFPredicate::OEQ:381      result = checkResult(llvm::APFloat::cmpEqual);382      break;383    case arith::CmpFPredicate::OGT:384      result = checkResult(llvm::APFloat::cmpGreaterThan);385      break;386    case arith::CmpFPredicate::OGE:387      result = checkResults(388          {llvm::APFloat::cmpGreaterThan, llvm::APFloat::cmpEqual});389      break;390    case arith::CmpFPredicate::OLT:391      result = checkResult(llvm::APFloat::cmpLessThan);392      break;393    case arith::CmpFPredicate::OLE:394      result =395          checkResults({llvm::APFloat::cmpLessThan, llvm::APFloat::cmpEqual});396      break;397    case arith::CmpFPredicate::ONE:398      // Not cmpUnordered and not cmpUnordered.399      result = checkResults(400          {llvm::APFloat::cmpLessThan, llvm::APFloat::cmpGreaterThan});401      break;402    case arith::CmpFPredicate::ORD:403      // Not cmpUnordered.404      result = checkResults({llvm::APFloat::cmpLessThan,405                             llvm::APFloat::cmpGreaterThan,406                             llvm::APFloat::cmpEqual});407      break;408    case arith::CmpFPredicate::UEQ:409      result =410          checkResults({llvm::APFloat::cmpUnordered, llvm::APFloat::cmpEqual});411      break;412    case arith::CmpFPredicate::UGT:413      result = checkResults(414          {llvm::APFloat::cmpUnordered, llvm::APFloat::cmpGreaterThan});415      break;416    case arith::CmpFPredicate::UGE:417      result = checkResults({llvm::APFloat::cmpUnordered,418                             llvm::APFloat::cmpGreaterThan,419                             llvm::APFloat::cmpEqual});420      break;421    case arith::CmpFPredicate::ULT:422      result = checkResults(423          {llvm::APFloat::cmpUnordered, llvm::APFloat::cmpLessThan});424      break;425    case arith::CmpFPredicate::ULE:426      result =427          checkResults({llvm::APFloat::cmpUnordered, llvm::APFloat::cmpLessThan,428                        llvm::APFloat::cmpEqual});429      break;430    case arith::CmpFPredicate::UNE:431      // Not cmpEqual.432      result = checkResults({llvm::APFloat::cmpLessThan,433                             llvm::APFloat::cmpGreaterThan,434                             llvm::APFloat::cmpUnordered});435      break;436    case arith::CmpFPredicate::UNO:437      result = checkResult(llvm::APFloat::cmpUnordered);438      break;439    case arith::CmpFPredicate::AlwaysTrue:440      result = arith::ConstantOp::create(rewriter, loc, i1Type,441                                         rewriter.getIntegerAttr(i1Type, 1))442                   .getResult();443      break;444    }445    rewriter.replaceOp(op, result);446    return success();447  }448 449  SymbolOpInterface symTable;450};451 452struct NegFOpToAPFloatConversion final : OpRewritePattern<arith::NegFOp> {453  NegFOpToAPFloatConversion(MLIRContext *context, SymbolOpInterface symTable,454                            PatternBenefit benefit = 1)455      : OpRewritePattern<arith::NegFOp>(context, benefit), symTable(symTable) {}456 457  LogicalResult matchAndRewrite(arith::NegFOp op,458                                PatternRewriter &rewriter) const override {459    // Get APFloat function from runtime library.460    auto i32Type = IntegerType::get(symTable->getContext(), 32);461    auto i64Type = IntegerType::get(symTable->getContext(), 64);462    FailureOr<FuncOp> fn =463        lookupOrCreateApFloatFn(rewriter, symTable, "neg", {i32Type, i64Type});464    if (failed(fn))465      return fn;466 467    // Cast operand to 64-bit integer.468    rewriter.setInsertionPoint(op);469    Location loc = op.getLoc();470    auto floatTy = cast<FloatType>(op.getOperand().getType());471    auto intWType = rewriter.getIntegerType(floatTy.getWidth());472    Value operandBits = arith::ExtUIOp::create(473        rewriter, loc, i64Type, arith::BitcastOp::create(rewriter, loc, intWType, op.getOperand()));474 475    // Call APFloat function.476    Value semValue = getSemanticsValue(rewriter, loc, floatTy);477    SmallVector<Value> params = {semValue, operandBits};478    Value negatedBits =479        func::CallOp::create(rewriter, loc, TypeRange(i64Type),480                             SymbolRefAttr::get(*fn), params)481            ->getResult(0);482 483    // Truncate result to the original width.484    Value truncatedBits = arith::TruncIOp::create(rewriter, loc, intWType,485                                                  negatedBits);486    Value result =487        arith::BitcastOp::create(rewriter, loc, floatTy, truncatedBits);488    rewriter.replaceOp(op, result);489    return success();490  }491 492  SymbolOpInterface symTable;493};494 495namespace {496struct ArithToAPFloatConversionPass final497    : impl::ArithToAPFloatConversionPassBase<ArithToAPFloatConversionPass> {498  using Base::Base;499 500  void runOnOperation() override;501};502 503void ArithToAPFloatConversionPass::runOnOperation() {504  MLIRContext *context = &getContext();505  RewritePatternSet patterns(context);506  patterns.add<BinaryArithOpToAPFloatConversion<arith::AddFOp>>(context, "add",507                                                                getOperation());508  patterns.add<BinaryArithOpToAPFloatConversion<arith::SubFOp>>(509      context, "subtract", getOperation());510  patterns.add<BinaryArithOpToAPFloatConversion<arith::MulFOp>>(511      context, "multiply", getOperation());512  patterns.add<BinaryArithOpToAPFloatConversion<arith::DivFOp>>(513      context, "divide", getOperation());514  patterns.add<BinaryArithOpToAPFloatConversion<arith::RemFOp>>(515      context, "remainder", getOperation());516  patterns.add<BinaryArithOpToAPFloatConversion<arith::MinNumFOp>>(517      context, "minnum", getOperation());518  patterns.add<BinaryArithOpToAPFloatConversion<arith::MaxNumFOp>>(519      context, "maxnum", getOperation());520  patterns.add<BinaryArithOpToAPFloatConversion<arith::MinimumFOp>>(521      context, "minimum", getOperation());522  patterns.add<BinaryArithOpToAPFloatConversion<arith::MaximumFOp>>(523      context, "maximum", getOperation());524  patterns525      .add<FpToFpConversion<arith::ExtFOp>, FpToFpConversion<arith::TruncFOp>,526           CmpFOpToAPFloatConversion, NegFOpToAPFloatConversion>(527          context, getOperation());528  patterns.add<FpToIntConversion<arith::FPToSIOp>>(context, getOperation(),529                                                   /*isUnsigned=*/false);530  patterns.add<FpToIntConversion<arith::FPToUIOp>>(context, getOperation(),531                                                   /*isUnsigned=*/true);532  patterns.add<IntToFpConversion<arith::SIToFPOp>>(context, getOperation(),533                                                   /*isUnsigned=*/false);534  patterns.add<IntToFpConversion<arith::UIToFPOp>>(context, getOperation(),535                                                   /*isUnsigned=*/true);536  LogicalResult result = success();537  ScopedDiagnosticHandler scopedHandler(context, [&result](Diagnostic &diag) {538    if (diag.getSeverity() == DiagnosticSeverity::Error) {539      result = failure();540    }541    // NB: if you don't return failure, no other diag handlers will fire (see542    // mlir/lib/IR/Diagnostics.cpp:DiagnosticEngineImpl::emit).543    return failure();544  });545  walkAndApplyPatterns(getOperation(), std::move(patterns));546  if (failed(result))547    return signalPassFailure();548}549} // namespace550