// Copyright 2026 The TensorFlow Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ============================================================================== // RUN: litert-opt %s -tfl-bias-quantizer | FileCheck %s // CHECK-LABEL: QuantizeConv2DPerChannel func.func @QuantizeConv2DPerChannel(%arg0: tensor<1x224x224x3x!quant.uniform>, %arg1: tensor<32x3x3x3x!quant.uniform:f32:3, {1.0,2.0,3.0}>>) -> tensor<1x112x112x32xf32> { %bias = arith.constant dense<1.0> : tensor<32xf32> %input = "tfl.dequantize"(%arg0) : (tensor<1x224x224x3x!quant.uniform>) -> tensor<1x224x224x3xf32> %weight = "tfl.dequantize"(%arg1) : (tensor<32x3x3x3x!quant.uniform:f32:3, {1.0,2.0,3.0}>>) -> tensor<32x3x3x3xf32> %conv = "tfl.conv_2d"(%input, %weight, %bias) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x224x224x3xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x112x112x32xf32> func.return %conv : tensor<1x112x112x32xf32> // CHECK-NEXT: %[[cst:.*]] = arith.constant dense<1.000000e+00> : tensor<32xf32> // CHECK-NEXT: %[[qbias:.*]] = "tfl.quantize"(%[[cst]]) <{qtype = tensor<32x!quant.uniform>}> {propagated} // CHECK-NEXT: %[[bias:.*]] = "tfl.dequantize"(%[[qbias]]) // CHECK-NEXT: %[[in:.*]] = "tfl.dequantize"(%arg0) // CHECK-NEXT: %[[w:.*]] = "tfl.dequantize"(%arg1) // CHECK-NEXT: %[[conv:.*]] = "tfl.conv_2d"(%[[in]], %[[w]], %[[bias]]) // CHECK-NEXT: return %[[conv]] } // ----- // CHECK-LABEL: QuantizeConv2DPerChannelConst func.func @QuantizeConv2DPerChannelConst(%arg0: tensor<1x224x224x3x!quant.uniform>, %arg1: tensor<32x3x3x3x!quant.uniform:f32:3, {1.0,2.0,3.0}>>) -> tensor<1x112x112x32xf32> { %bias = "tfl.pseudo_const"() {value = dense<1.000000e+00> : tensor<32xf32>} : () -> tensor<32xf32> %input = "tfl.dequantize"(%arg0) : (tensor<1x224x224x3x!quant.uniform>) -> tensor<1x224x224x3xf32> %weight = "tfl.dequantize"(%arg1) : (tensor<32x3x3x3x!quant.uniform:f32:3, {1.0,2.0,3.0}>>) -> tensor<32x3x3x3xf32> %conv = "tfl.conv_2d"(%input, %weight, %bias) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x224x224x3xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x112x112x32xf32> func.return %conv : tensor<1x112x112x32xf32> // CHECK-NEXT: %[[cst:.*]] = "tfl.pseudo_const"() <{value = dense<1.000000e+00> : tensor<32xf32>}> : () -> tensor<32xf32> // CHECK-NEXT: %[[qbias:.*]] = "tfl.quantize"(%[[cst]]) <{qtype = tensor<32x!quant.uniform>}> {propagated} // CHECK-NEXT: %[[bias:.*]] = "tfl.dequantize"(%[[qbias]]) // CHECK-NEXT: %[[in:.*]] = "tfl.dequantize"(%arg0) // CHECK-NEXT: %[[w:.*]] = "tfl.dequantize"(%arg1) // CHECK-NEXT: %[[conv:.*]] = "tfl.conv_2d"(%[[in]], %[[w]], %[[bias]]) // CHECK-NEXT: return %[[conv]] } // ----- // CHECK-LABEL: QuantizeConv2DPerChannels func.func @QuantizeConv2DPerChannels(%arg0: tensor<1x224x224x3x!quant.uniform>, %arg1: tensor<32x3x3x3x!quant.uniform:f32:3, {1.0,2.0,3.0}>>) -> tensor<1x112x112x32xf32> { %bias = arith.constant dense<1.0> : tensor<32xf32> %input = "tfl.dequantize"(%arg0) : (tensor<1x224x224x3x!quant.uniform>) -> tensor<1x224x224x3xf32> %weight = "tfl.dequantize"(%arg1) : (tensor<32x3x3x3x!quant.uniform:f32:3, {1.0,2.0,3.0}>>) -> tensor<32x3x3x3xf32> %conv = "tfl.conv_2d"(%input, %weight, %bias) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x224x224x3xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x112x112x32xf32> func.return %conv : tensor<1x112x112x32xf32> // CHECK-NEXT: %[[cst:.*]] = arith.constant dense<1.000000e+00> : tensor<32xf32> // CHECK-NEXT: %[[qbias:.*]] = "tfl.quantize"(%[[cst]]) <{qtype = tensor<32x!quant.uniform>}> {propagated} // CHECK-NEXT: %[[bias:.*]] = "tfl.dequantize"(%[[qbias]]) // CHECK-NEXT: %[[in:.*]] = "tfl.dequantize"(%arg0) // CHECK-NEXT: %[[w:.*]] = "tfl.dequantize"(%arg1) // CHECK-NEXT: %[[conv:.*]] = "tfl.conv_2d"(%[[in]], %[[w]], %[[bias]]) // CHECK-NEXT: return %[[conv]] } // ----- // CHECK-LABEL: QuantizeConv2D func.func @QuantizeConv2D(tensor<1x224x224x3x!quant.uniform>) -> tensor<1x112x112x32x!quant.uniform> { ^bb0(%arg0: tensor<1x224x224x3x!quant.uniform>): %cst = arith.constant dense<-1.23697901> : tensor<32xf32> %2 = "tfl.dequantize"(%arg0) : (tensor<1x224x224x3x!quant.uniform>) -> tensor<1x224x224x3xf32> %3 = "tfl.pseudo_qconst"() {qtype = tensor<32x3x3x3x!quant.uniform:f32, 0.021826678373682216:151>>, value = dense<-76> : tensor<32x3x3x3xi8>} : () -> tensor<32x3x3x3x!quant.uniform:f32, 0.021826678373682216:151>> %4 = "tfl.dequantize"(%3) : (tensor<32x3x3x3x!quant.uniform:f32, 0.021826678373682216:151>>) -> tensor<32x3x3x3xf32> %5 = "tfl.conv_2d"(%2, %4, %cst) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x224x224x3xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x112x112x32xf32> %6 = "tfl.quantize"(%5) {qtype = tensor<1x112x112x32x!quant.uniform>} : (tensor<1x112x112x32xf32>) -> tensor<1x112x112x32x!quant.uniform> func.return %6 : tensor<1x112x112x32x!quant.uniform> // CHECK: %cst = arith.constant dense<-1.23697901> : tensor<32xf32> // CHECK: %0 = "tfl.quantize"(%cst) <{qtype = tensor<32x!quant.uniform>}> {propagated} // CHECK: %1 = "tfl.dequantize"(%0) : (tensor<32x!quant.uniform>) // CHECK: %2 = "tfl.dequantize"(%arg0) // CHECK: %3 = "tfl.pseudo_qconst"() // CHECK: %4 = "tfl.dequantize"(%3) // CHECK: %5 = "tfl.conv_2d"(%2, %4, %1) // CHECK: %6 = "tfl.quantize"(%5) // CHECK: return %6 } // ----- // CHECK-LABEL: QuantizeFullyConnected func.func @QuantizeFullyConnected(tensor<1x224x224x3x!quant.uniform>) -> tensor<1x112x112x32x!quant.uniform> { ^bb0(%arg0: tensor<1x224x224x3x!quant.uniform>): %cst = arith.constant dense<-1.23697901> : tensor<32xf32> %2 = "tfl.dequantize"(%arg0) : (tensor<1x224x224x3x!quant.uniform>) -> tensor<1x224x224x3xf32> %3 = "tfl.pseudo_qconst"() {qtype = tensor<32x3x3x3x!quant.uniform:f32, 0.021826678373682216:151>>, value = dense<-76> : tensor<32x12xi8>} : () -> tensor<32x12x!quant.uniform:f32, 0.021826678373682216:151>> %4 = "tfl.dequantize"(%3) : (tensor<32x12x!quant.uniform:f32, 0.021826678373682216:151>>) -> tensor<32x12xf32> %5 = "tfl.fully_connected"(%2, %4, %cst) {fused_activation_function = "NONE", keep_num_dims = false, weights_format = "DEFAULT"} : (tensor<1x224x224x3xf32>, tensor<32x12xf32>, tensor<32xf32>) -> tensor<1x112x112x32xf32> %6 = "tfl.quantize"(%5) {qtype = tensor<1x112x112x32x!quant.uniform>} : (tensor<1x112x112x32xf32>) -> tensor<1x112x112x32x!quant.uniform> func.return %6 : tensor<1x112x112x32x!quant.uniform> // CHECK: %cst = arith.constant dense<-1.23697901> : tensor<32xf32> // CHECK: %0 = "tfl.quantize"(%cst) <{qtype = tensor<32x!quant.uniform>}> {propagated} // CHECK: %1 = "tfl.dequantize"(%0) : (tensor<32x!quant.uniform>) // CHECK: %2 = "tfl.dequantize"(%arg0) // CHECK: %3 = "tfl.pseudo_qconst"() // CHECK: %4 = "tfl.dequantize"(%3) // CHECK: %5 = "tfl.fully_connected"(%2, %4, %1) // CHECK: %6 = "tfl.quantize"(%5) // CHECK: return %6 } // ----- // CHECK-LABEL: QuantizeDepthwiseConv2D func.func @QuantizeDepthwiseConv2D(tensor<1x224x224x3x!quant.uniform>) -> tensor<1x112x112x32x!quant.uniform> { ^bb0(%arg0: tensor<1x224x224x3x!quant.uniform>): %cst = arith.constant dense<-1.23697901> : tensor<32xf32> %2 = "tfl.dequantize"(%arg0) : (tensor<1x224x224x3x!quant.uniform>) -> tensor<1x224x224x3xf32> %3 = "tfl.pseudo_qconst"() {qtype = tensor<32x3x3x3x!quant.uniform:f32, 0.021826678373682216:151>>, value = dense<-76> : tensor<32x3x3x3xi8>} : () -> tensor<32x3x3x3x!quant.uniform:f32, 0.021826678373682216:151>> %4 = "tfl.dequantize"(%3) : (tensor<32x3x3x3x!quant.uniform:f32, 0.021826678373682216:151>>) -> tensor<32x3x3x3xf32> %5 = "tfl.depthwise_conv_2d"(%2, %4, %cst) {depth_multiplier = 4 : i32, dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "VALID", stride_h = 4 : i32, stride_w = 5 : i32} : (tensor<1x224x224x3xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x112x112x32xf32> %6 = "tfl.quantize"(%5) {qtype = tensor<1x112x112x32x!quant.uniform>} : (tensor<1x112x112x32xf32>) -> tensor<1x112x112x32x!quant.uniform> func.return %6 : tensor<1x112x112x32x!quant.uniform> // CHECK: %cst = arith.constant dense<-1.23697901> : tensor<32xf32> // CHECK: %0 = "tfl.quantize"(%cst) <{qtype = tensor<32x!quant.uniform>}> {propagated} // CHECK: %1 = "tfl.dequantize"(%0) : (tensor<32x!quant.uniform>) // CHECK: %2 = "tfl.dequantize"(%arg0) // CHECK: %3 = "tfl.pseudo_qconst"() // CHECK: %4 = "tfl.dequantize"(%3) // CHECK: %5 = "tfl.depthwise_conv_2d"(%2, %4, %1) // CHECK: %6 = "tfl.quantize"(%5) // CHECK: return %6 } // ----- // CHECK-LABEL: QuantizeSharedBiases func.func @QuantizeSharedBiases( %arg0: tensor<1x224x224x3x!quant.uniform>, %arg1: tensor<32x3x3x3x!quant.uniform:f32, 1.0>>, %arg2: tensor<32x3x3x3x!quant.uniform:f32, 2.0>>) -> (tensor<1x56x56x32x!quant.uniform>) { %cst = arith.constant dense<1.0> : tensor<32xf32> %1 = "tfl.dequantize"(%arg0) : (tensor<1x224x224x3x!quant.uniform>) -> tensor<1x224x224x3xf32> %2 = "tfl.dequantize"(%arg1) : (tensor<32x3x3x3x!quant.uniform:f32, 1.0>>) -> tensor<32x3x3x3xf32> %conv1 = "tfl.conv_2d"(%1, %2, %cst) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x224x224x3xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x112x112x32xf32> %3 = "tfl.quantize"(%conv1) {qtype = tensor<1x112x112x32xf32>} : (tensor<1x112x112x32xf32>) -> tensor<1x112x112x32x!quant.uniform> %4 = "tfl.dequantize"(%3) : (tensor<1x112x112x32x!quant.uniform>) -> tensor<1x112x112x32xf32> %5 = "tfl.dequantize"(%arg2) : (tensor<32x3x3x3x!quant.uniform:f32, 2.0>>) -> tensor<32x3x3x3xf32> %conv2 = "tfl.conv_2d"(%4, %5, %cst) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x112x112x32xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x56x56x32xf32> %6 = "tfl.quantize"(%conv2) {qtype = tensor<1x56x56x32x!quant.uniform>} : (tensor<1x56x56x32xf32>) -> tensor<1x56x56x32x!quant.uniform> func.return %6 : tensor<1x56x56x32x!quant.uniform> // CHECK: %[[CST:.*]] = arith.constant dense<1.000000e+00> : tensor<32xf32> // CHECK-DAG: %[[Q1:.*]] = "tfl.quantize"(%[[CST]]) // CHECK-DAG: %[[DQ1:.*]] = "tfl.dequantize"(%[[Q1]]) : (tensor<32x!quant.uniform>) // CHECK-DAG: %[[Q2:.*]] = "tfl.quantize"(%[[CST]]) // CHECK-DAG: %[[DQ2:.*]] = "tfl.dequantize"(%[[Q2]]) : (tensor<32x!quant.uniform>) // CHECK-DAG: %{{.*}} = "tfl.conv_2d"(%{{.*}}, %{{.*}}, %[[DQ1]]) // CHECK-DAG: %{{.*}} = "tfl.conv_2d"(%{{.*}}, %{{.*}}, %[[DQ2]]) } // ----- // CHECK-LABEL: QuantizeSharedBiases2 func.func @QuantizeSharedBiases2( %arg0: tensor<32x!quant.uniform>, %arg1: tensor<1x112x112x32x!quant.uniform>, %arg2: tensor<32x3x3x3x!quant.uniform:f32, 2.0>>) -> (tensor<32x!quant.uniform>, tensor<1x56x56x32x!quant.uniform>) { %cst = arith.constant dense<1.0> : tensor<32xf32> %1 = "tfl.dequantize"(%arg0) : (tensor<32x!quant.uniform>) -> tensor<32xf32> %add = "tfl.add"(%1, %cst) {fused_activation_function = "NONE"} : (tensor<32xf32>, tensor<32xf32>) -> tensor<32xf32> %3 = "tfl.quantize"(%add) {qtype = tensor<32xf32>} : (tensor<32xf32>) -> tensor<32x!quant.uniform> %5 = "tfl.dequantize"(%arg1) : (tensor<1x112x112x32x!quant.uniform>) -> tensor<1x112x112x32xf32> %6 = "tfl.dequantize"(%arg2) : (tensor<32x3x3x3x!quant.uniform:f32, 2.0>>) -> tensor<32x3x3x3xf32> %conv2 = "tfl.conv_2d"(%5, %6, %cst) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x112x112x32xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x56x56x32xf32> %7 = "tfl.quantize"(%conv2) {qtype = tensor<1x56x56x32x!quant.uniform>} : (tensor<1x56x56x32xf32>) -> tensor<1x56x56x32x!quant.uniform> func.return %3, %7 : tensor<32x!quant.uniform>, tensor<1x56x56x32x!quant.uniform> // CHECK: %[[CST:.*]] = arith.constant dense<1.000000e+00> : tensor<32xf32> // CHECK: %[[Q_BIAS:.*]] = "tfl.quantize"(%[[CST]]) <{qtype = tensor<32x!quant.uniform>}> {propagated} : (tensor<32xf32>) -> tensor<32x!quant.uniform> // CHECK: %[[DQ_BIAS:.*]] = "tfl.dequantize"(%[[Q_BIAS]]) // CHECK-DAG: tfl.add {{.*}}, %[[CST]] // CHECK-DAG: "tfl.conv_2d"({{.*}}, {{.*}}, %[[DQ_BIAS]]) } // ----- // Make sure biases are not shared. // CHECK-LABEL: QuantizeSharedBiases3 func.func @QuantizeSharedBiases3( %arg0: tensor<32x!quant.uniform>, %arg1: tensor<1x112x112x32x!quant.uniform>, %arg2: tensor<32x3x3x3x!quant.uniform:f32, 2.0>>) -> (tensor<32x!quant.uniform>, tensor<1x56x56x32x!quant.uniform>) { %cst = arith.constant dense<1.0> : tensor<32xf32> %5 = "tfl.dequantize"(%arg1) : (tensor<1x112x112x32x!quant.uniform>) -> tensor<1x112x112x32xf32> %6 = "tfl.dequantize"(%arg2) : (tensor<32x3x3x3x!quant.uniform:f32, 2.0>>) -> tensor<32x3x3x3xf32> %conv2 = "tfl.conv_2d"(%5, %6, %cst) {dilation_h_factor = 1 : i32, dilation_w_factor = 1 : i32, fused_activation_function = "NONE", padding = "SAME", stride_h = 2 : i32, stride_w = 2 : i32} : (tensor<1x112x112x32xf32>, tensor<32x3x3x3xf32>, tensor<32xf32>) -> tensor<1x56x56x32xf32> %7 = "tfl.quantize"(%conv2) {qtype = tensor<1x56x56x32x!quant.uniform>} : (tensor<1x56x56x32xf32>) -> tensor<1x56x56x32x!quant.uniform> %1 = "tfl.dequantize"(%arg0) : (tensor<32x!quant.uniform>) -> tensor<32xf32> %add = "tfl.add"(%1, %cst) {fused_activation_function = "NONE"} : (tensor<32xf32>, tensor<32xf32>) -> tensor<32xf32> %3 = "tfl.quantize"(%add) {qtype = tensor<32xf32>} : (tensor<32xf32>) -> tensor<32x!quant.uniform> func.return %3, %7 : tensor<32x!quant.uniform>, tensor<1x56x56x32x!quant.uniform> // CHECK: %[[CST:.*]] = arith.constant dense<1.000000e+00> : tensor<32xf32> // CHECK: %[[Q_BIAS:.*]] = "tfl.quantize"(%[[CST]]) <{qtype = tensor<32x!quant.uniform>}> // CHECK: %[[DQ_BIAS:.*]] = "tfl.dequantize"(%[[Q_BIAS]]) // CHECK-DAG: "tfl.conv_2d"({{.*}}, {{.*}}, %[[DQ_BIAS]]) // CHECK-DAG: tfl.add {{.*}}, %[[CST]] }