1use super::Interpreter;
2use crate::{
3 constraints::reg_key::*,
4 error::SimpleResult,
5 prelude::{
6 Bug,
7 BugVariant,
8 },
9 profiler::Profiler,
10};
11
12use fuel_asm::{
13 PanicReason,
14 RegId,
15};
16use fuel_tx::DependentCost;
17use fuel_types::{
18 ContractId,
19 Word,
20};
21
22#[cfg(test)]
23mod tests;
24
25impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal> {
26 pub fn remaining_gas(&self) -> Word {
28 self.registers[RegId::GGAS]
29 }
30
31 pub(crate) fn set_gas(&mut self, gas: Word) {
35 self.registers[RegId::GGAS] = gas;
36 self.registers[RegId::CGAS] = gas;
37 }
38
39 pub(crate) fn dependent_gas_charge(
40 &mut self,
41 gas_cost: DependentCost,
42 arg: Word,
43 ) -> SimpleResult<()> {
44 let current_contract = self.contract_id();
45 let SystemRegisters {
46 pc, ggas, cgas, is, ..
47 } = split_registers(&mut self.registers).0;
48 let profiler = ProfileGas {
49 pc: pc.as_ref(),
50 is: is.as_ref(),
51 current_contract,
52 profiler: &mut self.profiler,
53 };
54 dependent_gas_charge(cgas, ggas, profiler, gas_cost, arg)
55 }
56
57 pub(crate) fn dependent_gas_charge_without_base(
58 &mut self,
59 gas_cost: DependentCost,
60 arg: Word,
61 ) -> SimpleResult<()> {
62 let current_contract = self.contract_id();
63 let SystemRegisters {
64 pc, ggas, cgas, is, ..
65 } = split_registers(&mut self.registers).0;
66 let profiler = ProfileGas {
67 pc: pc.as_ref(),
68 is: is.as_ref(),
69 current_contract,
70 profiler: &mut self.profiler,
71 };
72 dependent_gas_charge_without_base(cgas, ggas, profiler, gas_cost, arg)
73 }
74
75 pub fn gas_charge(&mut self, gas: Word) -> SimpleResult<()> {
77 let current_contract = self.contract_id();
78 let SystemRegisters {
79 pc, ggas, cgas, is, ..
80 } = split_registers(&mut self.registers).0;
81
82 let profiler = ProfileGas {
83 pc: pc.as_ref(),
84 is: is.as_ref(),
85 current_contract,
86 profiler: &mut self.profiler,
87 };
88 gas_charge(cgas, ggas, profiler, gas)
89 }
90}
91
92pub(crate) fn dependent_gas_charge_without_base(
93 mut cgas: RegMut<CGAS>,
94 ggas: RegMut<GGAS>,
95 mut profiler: ProfileGas<'_>,
96 gas_cost: DependentCost,
97 arg: Word,
98) -> SimpleResult<()> {
99 let cost = gas_cost.resolve_without_base(arg);
100 profiler.profile(cgas.as_ref(), cost);
101 gas_charge_inner(cgas.as_mut(), ggas, cost)
102}
103
104pub(crate) fn dependent_gas_charge(
105 mut cgas: RegMut<CGAS>,
106 ggas: RegMut<GGAS>,
107 mut profiler: ProfileGas<'_>,
108 gas_cost: DependentCost,
109 arg: Word,
110) -> SimpleResult<()> {
111 let cost = gas_cost.resolve(arg);
112 profiler.profile(cgas.as_ref(), cost);
113 gas_charge_inner(cgas.as_mut(), ggas, cost)
114}
115
116pub(crate) fn gas_charge(
117 cgas: RegMut<CGAS>,
118 ggas: RegMut<GGAS>,
119 mut profiler: ProfileGas<'_>,
120 gas: Word,
121) -> SimpleResult<()> {
122 profiler.profile(cgas.as_ref(), gas);
123 gas_charge_inner(cgas, ggas, gas)
124}
125
126fn gas_charge_inner(
127 mut cgas: RegMut<CGAS>,
128 mut ggas: RegMut<GGAS>,
129 gas: Word,
130) -> SimpleResult<()> {
131 if *cgas > *ggas {
132 Err(Bug::new(BugVariant::GlobalGasLessThanContext).into())
133 } else if gas > *cgas {
134 *ggas = (*ggas)
135 .checked_sub(*cgas)
136 .ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
137 *cgas = 0;
138
139 Err(PanicReason::OutOfGas.into())
140 } else {
141 *cgas = (*cgas)
142 .checked_sub(gas)
143 .ok_or_else(|| Bug::new(BugVariant::ContextGasUnderflow))?;
144 *ggas = (*ggas)
145 .checked_sub(gas)
146 .ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
147
148 Ok(())
149 }
150}
151
152#[allow(dead_code)]
153pub(crate) struct ProfileGas<'a> {
154 pub pc: Reg<'a, PC>,
155 pub is: Reg<'a, IS>,
156 pub current_contract: Option<ContractId>,
157 pub profiler: &'a mut Profiler,
158}
159
160impl<'a> ProfileGas<'a> {
161 #[allow(unused_variables)]
162 pub(crate) fn profile(&mut self, cgas: Reg<CGAS>, gas: Word) {
163 #[cfg(feature = "profile-coverage")]
164 {
165 let location =
166 super::current_location(self.current_contract, self.pc, self.is);
167 self.profiler.set_coverage(location);
168 }
169
170 #[cfg(feature = "profile-gas")]
171 {
172 let gas_use = gas.min(*cgas);
173 let location =
174 super::current_location(self.current_contract, self.pc, self.is);
175 self.profiler.add_gas(location, gas_use);
176 }
177 }
178}